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
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>
289 lines
6.4 KiB
Go
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
|
|
}
|