package template import ( "reflect" "sort" "git.warky.dev/wdevs/relspecgo/pkg/reflectutil" ) // EnumeratedItem represents an item with its index type EnumeratedItem struct { Index int Value interface{} } // Enumerate returns a slice with index-value pairs // Usage: {{ range enumerate .Tables }}{{ .Index }}: {{ .Value.Name }}{{ end }} func Enumerate(slice interface{}) []EnumeratedItem { v := reflect.ValueOf(slice) if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { return []EnumeratedItem{} } result := make([]EnumeratedItem, v.Len()) for i := 0; i < v.Len(); i++ { result[i] = EnumeratedItem{ Index: i, Value: v.Index(i).Interface(), } } return result } // Batch splits a slice into chunks of specified size // Usage: {{ range batch .Columns 3 }}...{{ end }} func Batch(slice interface{}, size int) [][]interface{} { if size <= 0 { return [][]interface{}{} } v := reflect.ValueOf(slice) if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { return [][]interface{}{} } length := v.Len() if length == 0 { return [][]interface{}{} } numBatches := (length + size - 1) / size result := make([][]interface{}, numBatches) for i := 0; i < numBatches; i++ { start := i * size end := start + size if end > length { end = length } batch := make([]interface{}, end-start) for j := start; j < end; j++ { batch[j-start] = v.Index(j).Interface() } result[i] = batch } return result } // Chunk is an alias for Batch func Chunk(slice interface{}, size int) [][]interface{} { return Batch(slice, size) } // Reverse reverses a slice // Usage: {{ range reverse .Tables }}...{{ end }} func Reverse(slice interface{}) []interface{} { v := reflect.ValueOf(slice) if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { return []interface{}{} } length := v.Len() result := make([]interface{}, length) for i := 0; i < length; i++ { result[length-1-i] = v.Index(i).Interface() } return result } // First returns the first N items from a slice // Usage: {{ range first .Tables 5 }}...{{ end }} func First(slice interface{}, n int) []interface{} { if n <= 0 { return []interface{}{} } v := reflect.ValueOf(slice) if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { return []interface{}{} } length := v.Len() if n > length { n = length } result := make([]interface{}, n) for i := 0; i < n; i++ { result[i] = v.Index(i).Interface() } return result } // Last returns the last N items from a slice // Usage: {{ range last .Tables 5 }}...{{ end }} func Last(slice interface{}, n int) []interface{} { if n <= 0 { return []interface{}{} } v := reflect.ValueOf(slice) if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { return []interface{}{} } length := v.Len() if n > length { n = length } result := make([]interface{}, n) start := length - n for i := 0; i < n; i++ { result[i] = v.Index(start + i).Interface() } return result } // Skip skips the first N items and returns the rest // Usage: {{ range skip .Tables 2 }}...{{ end }} func Skip(slice interface{}, n int) []interface{} { if n < 0 { n = 0 } v := reflect.ValueOf(slice) if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { return []interface{}{} } length := v.Len() if n >= length { return []interface{}{} } result := make([]interface{}, length-n) for i := n; i < length; i++ { result[i-n] = v.Index(i).Interface() } return result } // Take returns the first N items (alias for First) // Usage: {{ range take .Tables 5 }}...{{ end }} func Take(slice interface{}, n int) []interface{} { return First(slice, n) } // Concat concatenates multiple slices // Usage: {{ $all := concat .Schema1.Tables .Schema2.Tables }} func Concat(slices ...interface{}) []interface{} { result := make([]interface{}, 0) for _, slice := range slices { v := reflect.ValueOf(slice) if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { continue } for i := 0; i < v.Len(); i++ { result = append(result, v.Index(i).Interface()) } } return result } // Unique removes duplicates from a slice (compares by string representation) // Usage: {{ $unique := unique .Items }} func Unique(slice interface{}) []interface{} { v := reflect.ValueOf(slice) if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { return []interface{}{} } seen := make(map[interface{}]bool) result := make([]interface{}, 0) for i := 0; i < v.Len(); i++ { item := v.Index(i).Interface() if !seen[item] { seen[item] = true result = append(result, item) } } return result } // SortBy sorts a slice by a field name (for structs) or key (for maps) // Usage: {{ $sorted := sortBy .Tables "Name" }} // Note: This is a basic implementation that works for simple cases func SortBy(slice interface{}, field string) []interface{} { v := reflect.ValueOf(slice) if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { return []interface{}{} } // Convert to interface slice result := make([]interface{}, v.Len()) for i := 0; i < v.Len(); i++ { result[i] = v.Index(i).Interface() } // Sort by field sort.Slice(result, func(i, j int) bool { vi := getFieldValue(result[i], field) vj := getFieldValue(result[j], field) return compareValues(vi, vj) < 0 }) return result } // GroupBy groups a slice by a field value // Usage: {{ $grouped := groupBy .Tables "Schema" }} func GroupBy(slice interface{}, field string) map[interface{}][]interface{} { v := reflect.ValueOf(slice) if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { return map[interface{}][]interface{}{} } result := make(map[interface{}][]interface{}) for i := 0; i < v.Len(); i++ { item := v.Index(i).Interface() key := getFieldValue(item, field) result[key] = append(result[key], item) } return result } // CountIf counts items matching a condition // Note: Since templates can't pass functions, this is limited // Usage in code (not directly in templates) func CountIf(slice interface{}, matches func(interface{}) bool) int { v := reflect.ValueOf(slice) if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { return 0 } count := 0 for i := 0; i < v.Len(); i++ { if matches(v.Index(i).Interface()) { count++ } } return count } // getFieldValue extracts a field value from a struct, map, or pointer func getFieldValue(item interface{}, field string) interface{} { return reflectutil.GetFieldValue(item, field) } // compareValues compares two values for sorting func compareValues(a, b interface{}) int { return reflectutil.CompareValues(a, b) }