Files
relspecgo/pkg/writers/template/loop_helpers.go
Hein 8c602e3db0
Some checks failed
CI / Lint (push) Successful in -27m59s
CI / Test (1.25) (push) Successful in -27m46s
CI / Test (1.24) (push) Failing after 59s
CI / Build (push) Successful in -28m14s
Integration Tests / Integration Tests (push) Failing after -28m16s
Release / Build and Release (push) Successful in 1m1s
Added go text template writier (#1)
feat(templ):  added templ to command line that reads go template and outputs code

Reviewed-on: #1
Co-authored-by: Hein <hein.puth@gmail.com>
Co-committed-by: Hein <hein.puth@gmail.com>
2026-01-03 19:05:53 +00:00

283 lines
6.5 KiB
Go

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)
}