Files
relspecgo/pkg/writers/template/safe_access.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

289 lines
6.4 KiB
Go

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
}