package template import ( "reflect" "git.warky.dev/wdevs/relspecgo/pkg/reflectutil" ) // Get safely gets a value from a map by key // Usage: {{ get .Metadata "key" }} func Get(m interface{}, key interface{}) interface{} { return reflectutil.MapGet(m, key) } // GetOr safely gets a value from a map with a default fallback // Usage: {{ getOr .Metadata "key" "default" }} func GetOr(m interface{}, key interface{}, defaultValue interface{}) interface{} { result := Get(m, key) if result == nil { return defaultValue } return result } // GetPath safely gets a nested value using dot notation // Usage: {{ getPath .Config "database.connection.host" }} func GetPath(m interface{}, path string) interface{} { return reflectutil.GetNestedValue(m, path) } // GetPathOr safely gets a nested value with a default fallback // Usage: {{ getPathOr .Config "database.connection.host" "localhost" }} func GetPathOr(m interface{}, path string, defaultValue interface{}) interface{} { result := GetPath(m, path) if result == nil { return defaultValue } return result } // SafeIndex safely gets an element from a slice by index // Usage: {{ safeIndex .Tables 0 }} func SafeIndex(slice interface{}, index int) interface{} { return reflectutil.SliceIndex(slice, index) } // SafeIndexOr safely gets an element from a slice with a default fallback // Usage: {{ safeIndexOr .Tables 0 "default" }} func SafeIndexOr(slice interface{}, index int, defaultValue interface{}) interface{} { result := SafeIndex(slice, index) if result == nil { return defaultValue } return result } // Has checks if a key exists in a map // Usage: {{ if has .Metadata "key" }}...{{ end }} func Has(m interface{}, key interface{}) bool { v := reflect.ValueOf(m) // Dereference pointers for v.Kind() == reflect.Ptr { if v.IsNil() { return false } v = v.Elem() } if v.Kind() != reflect.Map { return false } keyVal := reflect.ValueOf(key) return v.MapIndex(keyVal).IsValid() } // HasPath checks if a nested path exists // Usage: {{ if hasPath .Config "database.connection.host" }}...{{ end }} func HasPath(m interface{}, path string) bool { return GetPath(m, path) != nil } // Keys returns all keys from a map // Usage: {{ range keys .Metadata }}...{{ end }} func Keys(m interface{}) []interface{} { return reflectutil.MapKeys(m) } // Values returns all values from a map // Usage: {{ range values .Table.Columns }}...{{ end }} func Values(m interface{}) []interface{} { return reflectutil.MapValues(m) } // Merge merges multiple maps into a new map // Usage: {{ $merged := merge .Map1 .Map2 }} func Merge(maps ...interface{}) map[interface{}]interface{} { result := make(map[interface{}]interface{}) for _, m := range maps { v := reflect.ValueOf(m) // Dereference pointers for v.Kind() == reflect.Ptr { if v.IsNil() { continue } v = v.Elem() } if v.Kind() != reflect.Map { continue } iter := v.MapRange() for iter.Next() { result[iter.Key().Interface()] = iter.Value().Interface() } } return result } // Pick returns a new map with only the specified keys // Usage: {{ $subset := pick .Metadata "name" "description" }} func Pick(m interface{}, keys ...interface{}) map[interface{}]interface{} { result := make(map[interface{}]interface{}) v := reflect.ValueOf(m) // Dereference pointers for v.Kind() == reflect.Ptr { if v.IsNil() { return result } v = v.Elem() } if v.Kind() != reflect.Map { return result } for _, key := range keys { keyVal := reflect.ValueOf(key) mapVal := v.MapIndex(keyVal) if mapVal.IsValid() { result[key] = mapVal.Interface() } } return result } // Omit returns a new map without the specified keys // Usage: {{ $filtered := omit .Metadata "internal" "private" }} func Omit(m interface{}, keys ...interface{}) map[interface{}]interface{} { result := make(map[interface{}]interface{}) v := reflect.ValueOf(m) // Dereference pointers for v.Kind() == reflect.Ptr { if v.IsNil() { return result } v = v.Elem() } if v.Kind() != reflect.Map { return result } // Create a set of keys to omit omitSet := make(map[interface{}]bool) for _, key := range keys { omitSet[key] = true } // Add all keys that are not in the omit set iter := v.MapRange() for iter.Next() { key := iter.Key().Interface() if !omitSet[key] { result[key] = iter.Value().Interface() } } return result } // SliceContains checks if a slice contains a value // Usage: {{ if sliceContains .Names "admin" }}...{{ end }} func SliceContains(slice interface{}, value interface{}) bool { v := reflect.ValueOf(slice) v, ok := reflectutil.Deref(v) if !ok { return false } if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { return false } for i := 0; i < v.Len(); i++ { if reflectutil.DeepEqual(v.Index(i).Interface(), value) { return true } } return false } // IndexOf returns the index of a value in a slice, or -1 if not found // Usage: {{ $idx := indexOf .Names "admin" }} func IndexOf(slice interface{}, value interface{}) int { v := reflect.ValueOf(slice) v, ok := reflectutil.Deref(v) if !ok { return -1 } if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { return -1 } for i := 0; i < v.Len(); i++ { if reflectutil.DeepEqual(v.Index(i).Interface(), value) { return i } } return -1 } // Pluck extracts a field from each element in a slice // Usage: {{ $names := pluck .Tables "Name" }} func Pluck(slice interface{}, field string) []interface{} { v := reflect.ValueOf(slice) // Dereference pointers for v.Kind() == reflect.Ptr { if v.IsNil() { return []interface{}{} } v = v.Elem() } if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { return []interface{}{} } result := make([]interface{}, 0, v.Len()) for i := 0; i < v.Len(); i++ { item := v.Index(i) // Dereference item pointers for item.Kind() == reflect.Ptr { if item.IsNil() { result = append(result, nil) continue } item = item.Elem() } switch item.Kind() { case reflect.Struct: fieldVal := item.FieldByName(field) if fieldVal.IsValid() { result = append(result, fieldVal.Interface()) } else { result = append(result, nil) } case reflect.Map: keyVal := reflect.ValueOf(field) mapVal := item.MapIndex(keyVal) if mapVal.IsValid() { result = append(result, mapVal.Interface()) } else { result = append(result, nil) } default: result = append(result, nil) } } return result }