196 lines
3.8 KiB
Go
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)
|
||
|
|
}
|