1
0
bind/lib.go
2025-09-03 23:52:33 +02:00

187 lines
6.0 KiB
Go

// Package bind provides functions to work with flattened lists of structure fields.
package bind
import "reflect"
// Field type represents the possible types of a field.
type FieldType = reflect.Kind
const (
Bool FieldType = reflect.Bool
Int FieldType = reflect.Int
Int8 FieldType = reflect.Int8
Int16 FieldType = reflect.Int16
Int32 FieldType = reflect.Int32
Int64 FieldType = reflect.Int64
Uint FieldType = reflect.Uint
Uint8 FieldType = reflect.Uint8
Uint16 FieldType = reflect.Uint16
Uint32 FieldType = reflect.Uint32
Uint64 FieldType = reflect.Uint64
Uintptr FieldType = reflect.Uintptr
Float32 FieldType = reflect.Float32
Float64 FieldType = reflect.Float64
String FieldType = reflect.String
Any FieldType = reflect.Interface
Struct FieldType = reflect.Struct
Duration FieldType = 0xfffe
Time FieldType = 0xffff
)
// Field is a field of structure or a compatible map. It is used as the output of the inspection functions, and
// as the input of the binding functions.
type Field struct {
input bool
path []string
name string
list bool
typ reflect.Kind
free bool
value any
}
// BindScalar sets the receiver to the provided value or values. It traverses the receiver through wrappers like
// pointers, slices and interfaces. If the receiver contains a slice which is greater or equal length as the
// number of the provided values, N, it sets the first N slice items to the values. It returns true, if the
// values were set. It returns false, if the values were not set, e.g. due to incompatible types.
func BindScalar(receiver any, value ...any) bool {
return bindScalarReflect(receiver, value)
}
// BindScalarCreate is like BindScalar, but it allocates the receiver from the type T.
func BindScalarCreate[T any](value ...any) (T, bool) {
return bindScalarCreateReflect[T](value)
}
// BindScalarCreate is like BindScalar, but it allocates the receiver from the type t.
func BindScalarCreateWith(t reflect.Type, value ...any) (reflect.Value, bool) {
return reflect.Value{}, false
}
// ValueByPath defines a field for input to BindFields or BindFieldsCreate. It defines the field by its exact
// field path in the receiver structure.
func ValueByPath(path []string, value any) Field {
return Field{input: true, path: path, value: value}
}
// NamedValue defines a field for inptu to BindFields or BindFieldsCreate. The name is the kebab case
// representation of the field path. The struct boundaries in the field path are not represented in the name,
// which leads to potentially ambigous field references, depending on the receiver type structure and naming. In
// case of ambigous fields, the field that can match multiple structure fields, will be bound to each matched
// structure field.
func NamedValue(name string, value any) Field {
return Field{input: true, name: name, value: value}
}
// Path returns the structure path of a field.
func (f Field) Path() []string {
p := make([]string, len(f.path))
copy(p, f.path)
return p
}
// Name returns the structure path of a field concatenated and in kebab casing, unless defined otherwise by
// NamedValue.
func (f Field) Name() string {
if f.name != "" || len(f.path) == 0 {
return f.name
}
return nameFromPath(f.path)
}
// List indicates whether a field is wrapped in a slice and accpets multiple values.
func (f Field) List() bool { return f.list }
// Type returns the scalar type of the field.
func (f Field) Type() reflect.Kind {
if !f.input {
return f.typ
}
return scalarTypeReflect(f.value)
}
// Free indicates that the field was found in a map.
func (f Field) Free() bool {
return f.free
}
// Value returns the value of a field.
func (f Field) Value() any {
return f.value
}
// Fields returns the fields of a structure type recursively. It traverses through pointers and slices.
func Fields[T any]() []Field {
return fieldsReflect[T]()
}
// Fields of is like Fields but uses type t as the input.
func FieldsOf(t reflect.Type) []Field {
return nil
}
// FieldValues returns the fields of a structure value recursively. It traverses through pointers, slices and
// interfaces.
func FieldValues(structure any) []Field {
return fieldValuesReflect(structure)
}
// BindFields sets structure fields recursively. It traverses through poitners, slices and interfaces. It
// returns the values for which it is not possible to find a compatible matching field. It supports maps that
// have string keys and scalar values.
func BindFields(structure any, values ...Field) []Field {
return bindFieldsReflect(structure, values)
}
// BindFieldsCreate is like BindFields, but it allocates the receiver from type T.
func BindFieldsCreate[T any](values ...Field) (T, []Field) {
return bindFieldsCreateReflect[T](values)
}
// BindFieldsCreate is like BindFields, but it allocates the receiver from type t.
func BindFieldsCreateWith(t reflect.Type, values ...Field) (reflect.Value, []Field) {
return reflect.Value{}, nil
}
// AcceptsScalar checks if a type can be used with BindScalarCreate or the values of the type with BindScalar.
func AcceptsScalar[T any]() bool {
return acceptsScalarReflect[T]()
}
// TypeAcceptsScalar is like AcceptsScalar, but uses type t as input.
func TypeAcceptsScalar(t reflect.Type) bool {
return false
}
// AcceptsFields checks if a type can be used with BindFieldsCreate or the values of the type with BindFields.
func AcceptsFields[T any]() bool {
return acceptsFieldsReflect[T]()
}
// TypeAcceptsFields is like AcceptsFields, but uses type t as input.
func TypeAcceptsFields(t reflect.Type) bool {
return false
}
// AcceptsList checks if a type can be used to bind multiple values.
func AcceptsList[T any]() bool {
return acceptsListReflect[T]()
}
// TypeAcceptsList is like AcceptsList, but uses type t as input.
func TypeAcceptsList(t reflect.Type) bool {
return false
}
// Bindable is the same as AcceptsScalar[T]() || AcceptsFields[T]().
func Bindable[T any]() bool {
return bindableReflect[T]()
}
// BindableType is like Bindable, but uses type t as input.
func BindableType(t reflect.Type) bool {
return false
}