190 lines
6.2 KiB
Go
190 lines
6.2 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
|
|
Float32 FieldType = reflect.Float32
|
|
Float64 FieldType = reflect.Float64
|
|
String FieldType = reflect.String
|
|
Any FieldType = reflect.Interface
|
|
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)
|
|
}
|
|
|
|
// CreateAndBindScalar is like BindScalar, but it allocates the receiver from the type T.
|
|
func CreateAndBindScalar[T any](value ...any) (T, bool) {
|
|
return bindScalarCreateReflect[T](value)
|
|
}
|
|
|
|
// CreateAndBindScalarFor is like BindScalar, but it allocates the receiver from the type t.
|
|
func CreateAndBindScalarFor(t reflect.Type, value ...any) (reflect.Value, bool) {
|
|
return bindScalarCreate(t, value)
|
|
}
|
|
|
|
// CreateAndBindScalarFieldType is like BindScalar, but it allocates the receiver from the field type t.
|
|
func CreateAndBindScalarFieldType(t FieldType, value ...any) (reflect.Value, bool) {
|
|
return bindScalarCreateFieldType(t, value)
|
|
}
|
|
|
|
// 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() FieldType {
|
|
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 fieldsOf(t)
|
|
}
|
|
|
|
// FieldValues returns the fields of a structure value recursively. It traverses through pointers, slices and
|
|
// interfaces.
|
|
func FieldValues(structure any) []Field {
|
|
return fieldValuesReflect(structure)
|
|
}
|
|
|
|
// Bind sets structure fields recursively. It traverses through pointers, 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 Bind(structure any, value ...Field) []Field {
|
|
return bindFieldsReflect(structure, value)
|
|
}
|
|
|
|
// CreateAndBind is like Bind, but it allocates the receiver from type T.
|
|
func CreateAndBind[T any](value ...Field) (T, []Field) {
|
|
return bindFieldsCreateReflect[T](value)
|
|
}
|
|
|
|
// CreateAndBindFor is like Bind, but it allocates the receiver from type t.
|
|
func CreateAndBindFor(t reflect.Type, value ...Field) (reflect.Value, []Field) {
|
|
return bindFieldsCreate(t, value)
|
|
}
|
|
|
|
// AcceptsScalar checks if a type can be used with CreateScalarAndBind 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 acceptsScalarChecked(t)
|
|
}
|
|
|
|
// 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 acceptsFieldsChecked(t)
|
|
}
|
|
|
|
// 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 acceptsListChecked(t)
|
|
}
|
|
|
|
// 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 bindableChecked(t)
|
|
}
|