1
0
bind/lib.go

190 lines
6.2 KiB
Go
Raw Permalink Normal View History

2025-08-31 17:11:09 +02:00
// Package bind provides functions to work with flattened lists of structure fields.
2025-08-27 21:03:25 +02:00
package bind
import "reflect"
// Field type represents the possible types of a field.
type FieldType = reflect.Kind
2025-08-31 02:56:26 +02:00
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
2025-09-03 23:52:33 +02:00
String FieldType = reflect.String
Any FieldType = reflect.Interface
Duration FieldType = 0xfffe
Time FieldType = 0xffff
2025-08-31 02:56:26 +02:00
)
2025-08-31 17:11:09 +02:00
// 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.
2025-08-27 21:03:25 +02:00
type Field struct {
2025-08-31 17:11:09 +02:00
input bool
2025-08-31 02:56:26 +02:00
path []string
name string
list bool
typ reflect.Kind
2025-08-31 02:56:26 +02:00
free bool
value any
2025-08-27 21:03:25 +02:00
}
2025-08-31 17:11:09 +02:00
// 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.
2025-08-27 21:03:25 +02:00
func BindScalar(receiver any, value ...any) bool {
return bindScalarReflect(receiver, value)
}
2025-09-04 01:48:21 +02:00
// CreateAndBindScalar is like BindScalar, but it allocates the receiver from the type T.
2025-09-04 00:33:05 +02:00
func CreateAndBindScalar[T any](value ...any) (T, bool) {
2025-08-27 21:03:25 +02:00
return bindScalarCreateReflect[T](value)
}
2025-09-04 01:48:21 +02:00
// CreateAndBindScalarFor is like BindScalar, but it allocates the receiver from the type t.
2025-09-04 00:33:05 +02:00
func CreateAndBindScalarFor(t reflect.Type, value ...any) (reflect.Value, bool) {
return bindScalarCreate(t, value)
}
2025-09-04 01:48:21 +02:00
// 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)
}
2025-08-31 17:11:09 +02:00
// ValueByPath defines a field for input to BindFields or BindFieldsCreate. It defines the field by its exact
// field path in the receiver structure.
2025-08-31 00:40:47 +02:00
func ValueByPath(path []string, value any) Field {
2025-08-31 17:11:09 +02:00
return Field{input: true, path: path, value: value}
2025-08-27 21:03:25 +02:00
}
2025-08-31 17:11:09 +02:00
// 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.
2025-08-31 00:40:47 +02:00
func NamedValue(name string, value any) Field {
2025-08-31 17:11:09 +02:00
return Field{input: true, name: name, value: value}
2025-08-27 21:03:25 +02:00
}
2025-08-31 17:11:09 +02:00
// Path returns the structure path of a field.
2025-08-27 21:03:25 +02:00
func (f Field) Path() []string {
p := make([]string, len(f.path))
copy(p, f.path)
return p
}
2025-08-31 17:11:09 +02:00
// Name returns the structure path of a field concatenated and in kebab casing, unless defined otherwise by
// NamedValue.
2025-08-31 00:40:47 +02:00
func (f Field) Name() string {
if f.name != "" || len(f.path) == 0 {
return f.name
}
return nameFromPath(f.path)
}
2025-08-31 17:11:09 +02:00
// List indicates whether a field is wrapped in a slice and accpets multiple values.
func (f Field) List() bool { return f.list }
2025-08-27 21:03:25 +02:00
2025-08-31 17:11:09 +02:00
// Type returns the scalar type of the field.
2025-11-25 14:51:23 +01:00
func (f Field) Type() FieldType {
2025-08-31 17:11:09 +02:00
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.
2025-08-27 21:03:25 +02:00
func Fields[T any]() []Field {
2025-08-28 05:04:06 +02:00
return fieldsReflect[T]()
2025-08-27 21:03:25 +02:00
}
// Fields of is like Fields but uses type t as the input.
func FieldsOf(t reflect.Type) []Field {
2025-09-04 00:33:05 +02:00
return fieldsOf(t)
}
2025-08-31 17:11:09 +02:00
// FieldValues returns the fields of a structure value recursively. It traverses through pointers, slices and
// interfaces.
2025-08-27 21:03:25 +02:00
func FieldValues(structure any) []Field {
2025-08-28 05:04:06 +02:00
return fieldValuesReflect(structure)
2025-08-27 21:03:25 +02:00
}
2025-11-05 19:16:44 +01:00
// Bind sets structure fields recursively. It traverses through pointers, slices and interfaces. It returns the
2025-09-04 00:33:05 +02:00
// 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)
2025-08-27 21:03:25 +02:00
}
2025-09-04 00:33:05 +02:00
// CreateAndBind is like Bind, but it allocates the receiver from type T.
func CreateAndBind[T any](value ...Field) (T, []Field) {
return bindFieldsCreateReflect[T](value)
2025-08-27 21:03:25 +02:00
}
2025-09-04 00:33:05 +02:00
// 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)
}
2025-09-04 01:48:21 +02:00
// AcceptsScalar checks if a type can be used with CreateScalarAndBind or the values of the type with BindScalar.
2025-08-27 21:03:25 +02:00
func AcceptsScalar[T any]() bool {
return acceptsScalarReflect[T]()
}
// TypeAcceptsScalar is like AcceptsScalar, but uses type t as input.
func TypeAcceptsScalar(t reflect.Type) bool {
2025-09-04 00:33:05 +02:00
return acceptsScalarChecked(t)
}
2025-08-31 17:11:09 +02:00
// AcceptsFields checks if a type can be used with BindFieldsCreate or the values of the type with BindFields.
2025-08-27 21:03:25 +02:00
func AcceptsFields[T any]() bool {
return acceptsFieldsReflect[T]()
}
// TypeAcceptsFields is like AcceptsFields, but uses type t as input.
func TypeAcceptsFields(t reflect.Type) bool {
2025-09-04 00:33:05 +02:00
return acceptsFieldsChecked(t)
}
2025-08-31 17:11:09 +02:00
// AcceptsList checks if a type can be used to bind multiple values.
2025-08-27 21:03:25 +02:00
func AcceptsList[T any]() bool {
return acceptsListReflect[T]()
}
// TypeAcceptsList is like AcceptsList, but uses type t as input.
func TypeAcceptsList(t reflect.Type) bool {
2025-09-04 00:33:05 +02:00
return acceptsListChecked(t)
}
2025-08-31 17:11:09 +02:00
// Bindable is the same as AcceptsScalar[T]() || AcceptsFields[T]().
2025-08-27 21:03:25 +02:00
func Bindable[T any]() bool {
return bindableReflect[T]()
}
// BindableType is like Bindable, but uses type t as input.
func BindableType(t reflect.Type) bool {
2025-09-04 00:33:05 +02:00
return bindableChecked(t)
}