// 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() 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 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 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 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) }