1
0
bind/field.go
2025-08-28 05:04:06 +02:00

196 lines
3.8 KiB
Go

package bind
import (
"fmt"
"github.com/iancoleman/strcase"
"reflect"
"unicode"
)
func exported(name string) bool {
return unicode.IsUpper([]rune(name)[0])
}
func fields(t reflect.Type) []Field {
t = unpackType(t, pointer)
list := t.Kind() == reflect.Slice
t = unpackType(t, pointer|slice)
if t.Kind() != reflect.Struct {
return nil
}
var f []Field
for i := 0; i < t.NumField(); i++ {
tfi := t.Field(i)
if !exported(tfi.Name) && !tfi.Anonymous {
continue
}
if acceptsScalar(tfi.Type) {
var fi Field
ft := unpackType(tfi.Type, pointer|slice)
fi.isBool = ft.Kind() == reflect.Bool
fi.list = acceptsList(tfi.Type)
fi.name = strcase.ToKebab(tfi.Name)
fi.path = []string{tfi.Name}
f = append(f, fi)
continue
}
ffi := fields(tfi.Type)
if !tfi.Anonymous {
for i := range ffi {
ffi[i].name = fmt.Sprintf(
"%s-%s",
strcase.ToKebab(tfi.Name),
ffi[i].name,
)
ffi[i].path = append(
[]string{tfi.Name},
ffi[i].path...,
)
}
}
f = append(f, ffi...)
}
if list {
for i := range f {
f[i].list = true
}
}
return f
}
func fieldFromValue(name string, v reflect.Value) Field {
var fi Field
fi.name = strcase.ToKebab(name)
fi.path = []string{name}
fi.value = v.Interface()
fi.isBool = v.Kind() == reflect.Bool
return fi
}
func scalarMapFields(v reflect.Value) []Field {
var f []Field
for _, key := range v.MapKeys() {
value := v.MapIndex(key)
name := key.Interface().(string)
if value.Kind() == reflect.Slice {
for i := 0; i < value.Len(); i++ {
fi := fieldFromValue(name, value.Index(i))
fi.free = true
f = append(f, fi)
}
} else {
fi := fieldFromValue(name, value)
fi.free = true
f = append(f, fi)
}
}
return f
}
func prependFieldName(name string, f []Field) {
for i := range f {
f[i].name = fmt.Sprintf("%s-%s", strcase.ToKebab(name), f[i].name)
f[i].path = append([]string{name}, f[i].path...)
}
}
func listFieldValues(fieldName string, l reflect.Value) []Field {
var f []Field
for i := 0; i < l.Len(); i++ {
item := l.Index(i)
item = unpackValue(item, pointer|iface|anytype)
switch {
case isScalar(item.Type()):
f = append(f, fieldFromValue(fieldName, item))
case item.Kind() == reflect.Slice:
f = append(f, listFieldValues(fieldName, item)...)
case isScalarMap(item.Type()):
mf := fieldValues(item)
prependFieldName(fieldName, mf)
f = append(f, mf...)
case item.Kind() == reflect.Struct:
sf := fieldValues(item)
prependFieldName(fieldName, sf)
f = append(f, sf...)
}
}
return f
}
func fieldValues(v reflect.Value) []Field {
var f []Field
v = unpackValue(v, pointer|anytype|iface)
if v.Kind() == reflect.Slice {
for i := 0; i < v.Len(); i++ {
f = append(f, fieldValues(v.Index(i))...)
}
return f
}
t := v.Type()
if isScalarMap(t) {
return scalarMapFields(v)
}
if t.Kind() != reflect.Struct {
return nil
}
for i := 0; i < t.NumField(); i++ {
tfi := t.Field(i)
if !exported(tfi.Name) && !tfi.Anonymous {
continue
}
vfi := v.Field(i)
vfi = unpackValue(vfi, pointer|iface|anytype)
switch {
case isScalar(vfi.Type()):
f = append(f, fieldFromValue(tfi.Name, vfi))
case vfi.Kind() == reflect.Slice:
f = append(f, listFieldValues(tfi.Name, vfi)...)
case isScalarMap(vfi.Type()):
mf := fieldValues(vfi)
prependFieldName(tfi.Name, mf)
f = append(f, mf...)
case vfi.Kind() == reflect.Struct:
sf := fieldValues(vfi)
if !tfi.Anonymous {
prependFieldName(tfi.Name, sf)
}
f = append(f, sf...)
}
}
return f
}
func fieldsReflect[T any]() []Field {
t := reflect.TypeFor[T]()
if hasCircularType(nil, t) {
return nil
}
return fields(t)
}
func fieldValuesReflect(structure any) []Field {
v := reflect.ValueOf(structure)
if hasCircularReference(nil, v) {
return nil
}
return fieldValues(v)
}