1
0

refactor/rename

This commit is contained in:
Arpad Ryszka 2025-09-04 00:33:05 +02:00
parent c27b9c2daa
commit 8683d8ba40
7 changed files with 189 additions and 104 deletions

View File

@ -427,6 +427,18 @@ func fieldsReflect[T any]() []Field {
return fields(t)
}
func fieldsOf(t reflect.Type) []Field {
if t == nil {
return nil
}
if hasCircularType(t) {
return nil
}
return fields(t)
}
func fieldValuesReflect(structure any) []Field {
v := reflect.ValueOf(structure)
if hasCircularReference(v) {
@ -499,22 +511,18 @@ func bindFieldsReflect(structure any, values []Field) []Field {
return bindFields(receiver, values)
}
func bindFieldsCreateReflect[T any](values []Field) (T, []Field) {
t := reflect.TypeFor[T]()
func bindFieldsCreate(t reflect.Type, values []Field) (reflect.Value, []Field) {
if hasCircularType(t) {
var r T
return r, values
return reflect.Zero(t), values
}
if !acceptsFields(t) {
var r T
return r, values
return reflect.Zero(t), values
}
receiver, ok := allocate(t, 1)
if !ok {
var r T
return r, values
return reflect.Zero(t), values
}
unmatched := bindFields(receiver, values)
@ -522,5 +530,16 @@ func bindFieldsCreateReflect[T any](values []Field) (T, []Field) {
receiver = reflect.Zero(t)
}
return receiver, unmatched
}
func bindFieldsCreateReflect[T any](values []Field) (T, []Field) {
t := reflect.TypeFor[T]()
receiver, unmatched := bindFieldsCreate(t, values)
if len(unmatched) == len(values) {
var t T
return t, unmatched
}
return receiver.Interface().(T), unmatched
}

View File

@ -3,12 +3,13 @@ package bind_test
import (
"code.squareroundforest.org/arpio/bind"
"code.squareroundforest.org/arpio/notation"
"fmt"
"reflect"
"slices"
"sort"
"strings"
"testing"
"time"
"fmt"
)
func TestField(t *testing.T) {
@ -132,7 +133,7 @@ func TestField(t *testing.T) {
})
t.Run("list of struct", func(t *testing.T) {
type s struct{Foo []struct{Bar int}}
type s struct{ Foo []struct{ Bar int } }
f := bind.Fields[s]()
if len(f) != 1 || f[0].Name() != "foo-bar" || !f[0].List() {
t.Fatal()
@ -156,7 +157,7 @@ func TestField(t *testing.T) {
t.Fatal(notation.Sprint(f))
}
check := map[string]bind.FieldType {
check := map[string]bind.FieldType{
"b": bind.Bool,
"i": bind.Int,
"u": bind.Uint,
@ -173,6 +174,31 @@ func TestField(t *testing.T) {
}
}
})
t.Run("fields from reflection type", func(t *testing.T) {
t.Run("ok", func(t *testing.T) {
type s struct{ Foo int }
f := bind.FieldsOf(reflect.TypeFor[s]())
if len(f) != 1 || f[0].Name() != "foo" {
t.Fatal()
}
})
t.Run("nil type", func(t *testing.T) {
f := bind.FieldsOf(nil)
if len(f) != 0 {
t.Fatal()
}
})
t.Run("circular type", func(t *testing.T) {
type p *p
f := bind.FieldsOf(reflect.TypeFor[p]())
if len(f) != 0 {
t.Fatal()
}
})
})
})
t.Run("field values", func(t *testing.T) {
@ -405,7 +431,7 @@ func TestField(t *testing.T) {
})
t.Run("list of struct", func(t *testing.T) {
type s struct{Foo []struct{Bar int}}
type s struct{ Foo []struct{ Bar int } }
var v s
f := bind.FieldValues(v)
if len(f) != 1 || f[0].Name() != "foo-bar" || !f[0].List() {
@ -428,7 +454,7 @@ func TestField(t *testing.T) {
Bar int
}
var v s
if len(bind.BindFields(&v, bind.NamedValue("bar", 42))) != 1 {
if len(bind.Bind(&v, bind.NamedValue("bar", 42))) != 1 {
t.Fatal()
}
})
@ -437,7 +463,7 @@ func TestField(t *testing.T) {
type s struct{ Foo int }
type p *p
var v p
if len(bind.BindFields(&v, bind.NamedValue("foo", 42))) != 1 {
if len(bind.Bind(&v, bind.NamedValue("foo", 42))) != 1 {
t.Fatal()
}
})
@ -445,7 +471,7 @@ func TestField(t *testing.T) {
t.Run("set by name", func(t *testing.T) {
type s struct{ FooBar int }
var v s
u := bind.BindFields(&v, bind.NamedValue("foo-bar", 42))
u := bind.Bind(&v, bind.NamedValue("foo-bar", 42))
if len(u) != 0 || v.FooBar != 42 {
t.Fatal(notation.Sprint(u), notation.Sprint(v))
}
@ -454,7 +480,7 @@ func TestField(t *testing.T) {
t.Run("set by path", func(t *testing.T) {
type s struct{ FooBar int }
var v s
u := bind.BindFields(&v, bind.ValueByPath([]string{"FooBar"}, 42))
u := bind.Bind(&v, bind.ValueByPath([]string{"FooBar"}, 42))
if len(u) != 0 || v.FooBar != 42 {
t.Fatal(notation.Sprint(u), notation.Sprint(v))
}
@ -466,7 +492,7 @@ func TestField(t *testing.T) {
Bar int
}
var v s
u := bind.BindFields(
u := bind.Bind(
&v,
bind.NamedValue("foo", 21),
bind.NamedValue("baz", 42),
@ -480,7 +506,7 @@ func TestField(t *testing.T) {
t.Run("bind list", func(t *testing.T) {
type s struct{ Foo []int }
var v s
u := bind.BindFields(
u := bind.Bind(
&v,
bind.NamedValue("foo", 21),
bind.NamedValue("foo", 42),
@ -495,7 +521,7 @@ func TestField(t *testing.T) {
t.Run("bind list of structs", func(t *testing.T) {
type s struct{ Foo []struct{ Bar int } }
var v s
u := bind.BindFields(
u := bind.Bind(
&v,
bind.NamedValue("foo-bar", 21),
bind.NamedValue("foo-bar", 42),
@ -510,7 +536,7 @@ func TestField(t *testing.T) {
t.Run("bind list in list", func(t *testing.T) {
type s struct{ Foo []struct{ Bar []int } }
var v s
u := bind.BindFields(
u := bind.Bind(
&v,
bind.NamedValue("foo-bar", 21),
bind.NamedValue("foo-bar", 42),
@ -527,7 +553,7 @@ func TestField(t *testing.T) {
t.Run("list receiver", func(t *testing.T) {
var l []struct{ Foo int }
u := bind.BindFields(
u := bind.Bind(
&l,
bind.NamedValue("foo", 21),
bind.NamedValue("foo", 42),
@ -546,7 +572,7 @@ func TestField(t *testing.T) {
)
v := s1{[]s0{{1}, {2}}}
u := bind.BindFields(
u := bind.Bind(
v,
bind.NamedValue("foo-bar", 21),
bind.NamedValue("foo-bar", 42),
@ -565,7 +591,7 @@ func TestField(t *testing.T) {
)
v := s1{[]s0{{1}, {2}}}
u := bind.BindFields(
u := bind.Bind(
&v,
bind.NamedValue("foo-bar", 21),
bind.NamedValue("foo-bar", 42),
@ -584,7 +610,7 @@ func TestField(t *testing.T) {
)
v := s1{[]s0{{nil}, {nil}}}
u := bind.BindFields(
u := bind.Bind(
&v,
bind.NamedValue("foo-bar", 21),
bind.NamedValue("foo-bar", 42),
@ -598,7 +624,7 @@ func TestField(t *testing.T) {
t.Run("bind scalar map", func(t *testing.T) {
m := make(map[string]int)
u := bind.BindFields(m, bind.NamedValue("foo-bar", 21), bind.NamedValue("baz-qux", 42))
u := bind.Bind(m, bind.NamedValue("foo-bar", 21), bind.NamedValue("baz-qux", 42))
if len(u) != 0 || len(m) != 2 || m["foo-bar"] != 21 || m["baz-qux"] != 42 {
t.Fatal()
}
@ -607,7 +633,7 @@ func TestField(t *testing.T) {
t.Run("scalar map key conversion", func(t *testing.T) {
type key string
m := make(map[key]int)
u := bind.BindFields(m, bind.NamedValue("foo", 42))
u := bind.Bind(m, bind.NamedValue("foo", 42))
if len(u) != 0 || len(m) != 1 || m["foo"] != 42 {
t.Fatal()
}
@ -615,7 +641,7 @@ func TestField(t *testing.T) {
t.Run("scalar map pointer key", func(t *testing.T) {
m := make(map[*string]int)
u := bind.BindFields(m, bind.NamedValue("foo", 42))
u := bind.Bind(m, bind.NamedValue("foo", 42))
if len(u) != 0 || len(m) != 1 {
t.Fatal()
}
@ -629,7 +655,7 @@ func TestField(t *testing.T) {
t.Run("scalar map list value", func(t *testing.T) {
m := make(map[string][]int)
u := bind.BindFields(m, bind.NamedValue("foo", 21), bind.NamedValue("foo", 42), bind.NamedValue("foo", 84))
u := bind.Bind(m, bind.NamedValue("foo", 21), bind.NamedValue("foo", 42), bind.NamedValue("foo", 84))
if len(u) != 0 || len(m) != 1 || !slices.Equal(m["foo"], []int{21, 42, 84}) {
t.Fatal(notation.Sprint(u), notation.Sprint(m))
}
@ -637,7 +663,7 @@ func TestField(t *testing.T) {
t.Run("scalar map pointer value", func(t *testing.T) {
m := make(map[string]*int)
u := bind.BindFields(m, bind.NamedValue("foo", 42))
u := bind.Bind(m, bind.NamedValue("foo", 42))
if len(u) != 0 || len(m) != 1 {
t.Fatal()
}
@ -651,7 +677,7 @@ func TestField(t *testing.T) {
t.Run("scalar map list pointer value", func(t *testing.T) {
m := make(map[string]*[]int)
u := bind.BindFields(m, bind.NamedValue("foo", 21), bind.NamedValue("foo", 42), bind.NamedValue("foo", 84))
u := bind.Bind(m, bind.NamedValue("foo", 21), bind.NamedValue("foo", 42), bind.NamedValue("foo", 84))
if len(u) != 0 || len(m) != 1 || !slices.Equal(*m["foo"], []int{21, 42, 84}) {
t.Fatal()
}
@ -660,7 +686,7 @@ func TestField(t *testing.T) {
t.Run("allocate scalar map", func(t *testing.T) {
type s struct{ Foo map[string]int }
var v s
u := bind.BindFields(&v, bind.NamedValue("foo-bar", 42))
u := bind.Bind(&v, bind.NamedValue("foo-bar", 42))
if len(u) != 0 || len(v.Foo) != 1 || v.Foo["bar"] != 42 {
t.Fatal()
}
@ -669,7 +695,7 @@ func TestField(t *testing.T) {
t.Run("scalar map addressing via path", func(t *testing.T) {
type s struct{ Foo map[string]int }
var v s
u := bind.BindFields(&v, bind.ValueByPath([]string{"Foo", "Bar"}, 42))
u := bind.Bind(&v, bind.ValueByPath([]string{"Foo", "Bar"}, 42))
if len(u) != 0 || len(v.Foo) != 1 || v.Foo["Bar"] != 42 {
t.Fatal()
}
@ -677,7 +703,7 @@ func TestField(t *testing.T) {
t.Run("scalar map and too long field path", func(t *testing.T) {
m := make(map[string]int)
u := bind.BindFields(m, bind.ValueByPath([]string{"foo", "bar"}, 42))
u := bind.Bind(m, bind.ValueByPath([]string{"foo", "bar"}, 42))
if len(u) != 1 {
t.Fatal()
}
@ -686,7 +712,7 @@ func TestField(t *testing.T) {
t.Run("scalar map cannot be set", func(t *testing.T) {
type s struct{ Foo map[string]int }
var v s
u := bind.BindFields(v, bind.NamedValue("foo-bar", 42))
u := bind.Bind(v, bind.NamedValue("foo-bar", 42))
if len(u) != 1 {
t.Fatal()
}
@ -694,7 +720,7 @@ func TestField(t *testing.T) {
t.Run("scalar map with wrong value type", func(t *testing.T) {
m := make(map[string]int)
u := bind.BindFields(m, bind.NamedValue("foo", "bar"))
u := bind.Bind(m, bind.NamedValue("foo", "bar"))
if len(u) != 1 {
t.Fatal()
}
@ -706,7 +732,7 @@ func TestField(t *testing.T) {
Bar struct{ Baz string }
}
var v s
u := bind.BindFields(
u := bind.Bind(
&v,
bind.NamedValue("foo", 42),
bind.NamedValue("bar-baz", "qux"),
@ -723,7 +749,7 @@ func TestField(t *testing.T) {
Bar struct{ Baz string }
}
var v s
u := bind.BindFields(
u := bind.Bind(
&v,
bind.NamedValue("foo", 42),
bind.NamedValue("bar-qux", "qux"),
@ -740,7 +766,7 @@ func TestField(t *testing.T) {
Bar struct{ Baz string }
}
var v s
u := bind.BindFields(
u := bind.Bind(
&v,
bind.NamedValue("foo", 42),
bind.NamedValue("bar-baz", "qux"),
@ -760,7 +786,7 @@ func TestField(t *testing.T) {
}
var v s
u := bind.BindFields(
u := bind.Bind(
&v,
bind.NamedValue("foo", 42),
bind.NamedValue("bar-baz", "qux"),
@ -773,9 +799,9 @@ func TestField(t *testing.T) {
})
t.Run("pointer field with invalid value", func(t *testing.T) {
type s struct {Foo *struct{ Bar int }}
type s struct{ Foo *struct{ Bar int } }
var v s
u := bind.BindFields(
u := bind.Bind(
&v,
bind.NamedValue("foo-bar", "baz"),
)
@ -792,7 +818,7 @@ func TestField(t *testing.T) {
}
var v s
u := bind.BindFields(
u := bind.Bind(
&v,
bind.NamedValue("foo", 42),
bind.NamedValue("bar", "qux"),
@ -811,7 +837,7 @@ func TestField(t *testing.T) {
Bar struct{ Baz string }
}
var v s
u := bind.BindFields(
u := bind.Bind(
&v,
bind.ValueByPath([]string{"Foo"}, 42),
bind.ValueByPath([]string{"Bar", "Baz"}, "qux"),
@ -825,7 +851,7 @@ func TestField(t *testing.T) {
t.Run("cannot set field", func(t *testing.T) {
type s struct{ Foo int }
var v s
u := bind.BindFields(v, bind.NamedValue("foo", 42))
u := bind.Bind(v, bind.NamedValue("foo", 42))
if len(u) != 1 {
t.Fatal()
}
@ -838,7 +864,7 @@ func TestField(t *testing.T) {
)
var v s1
u := bind.BindFields(&v, bind.NamedValue("foo", 42))
u := bind.Bind(&v, bind.NamedValue("foo", 42))
if len(u) != 0 || v.Foo != 42 {
t.Fatal()
}
@ -847,7 +873,7 @@ func TestField(t *testing.T) {
t.Run("receiver cannot be set", func(t *testing.T) {
type s struct{ Foo *struct{ Bar int } }
var v s
u := bind.BindFields(v, bind.NamedValue("foo-bar", 42))
u := bind.Bind(v, bind.NamedValue("foo-bar", 42))
if len(u) != 1 {
t.Fatal()
}
@ -855,7 +881,7 @@ func TestField(t *testing.T) {
t.Run("receiver not supported", func(t *testing.T) {
v := make(chan int)
u := bind.BindFields(&v, bind.NamedValue("foo-bar", 42))
u := bind.Bind(&v, bind.NamedValue("foo-bar", 42))
if len(u) != 1 {
t.Fatal()
}
@ -863,7 +889,7 @@ func TestField(t *testing.T) {
t.Run("empty receiver", func(t *testing.T) {
var v any
u := bind.BindFields(&v, bind.NamedValue("foo", 42))
u := bind.Bind(&v, bind.NamedValue("foo", 42))
if len(u) != 1 {
t.Fatal()
}
@ -872,14 +898,14 @@ func TestField(t *testing.T) {
t.Run("nil value", func(t *testing.T) {
type s struct{ Foo any }
v := s{42}
u := bind.BindFields(&v, bind.NamedValue("foo", nil))
u := bind.Bind(&v, bind.NamedValue("foo", nil))
if len(u) != 0 || v.Foo != nil {
t.Fatal()
}
})
t.Run("nil receiver", func(t *testing.T) {
u := bind.BindFields(nil, bind.NamedValue("foo", nil))
u := bind.Bind(nil, bind.NamedValue("foo", nil))
if len(u) != 1 {
t.Fatal()
}
@ -892,7 +918,7 @@ func TestField(t *testing.T) {
}
var v s
u := bind.BindFields(&v, bind.NamedValue("foo-bar", 42))
u := bind.Bind(&v, bind.NamedValue("foo-bar", 42))
if len(u) != 0 || v.FooBar != 42 || v.Foo.Bar != 42 {
t.Fatal(notation.Sprint(u), notation.Sprint(v))
}
@ -900,18 +926,18 @@ func TestField(t *testing.T) {
t.Run("scalar map with invalid field type", func(t *testing.T) {
v := make(map[string]int)
u := bind.BindFields(v, bind.NamedValue("foo", "bar"))
u := bind.Bind(v, bind.NamedValue("foo", "bar"))
if len(u) != 1 {
t.Fatal()
}
})
t.Run("unpack interfaces", func(t *testing.T) {
type s struct{Foo time.Duration}
type s struct{ Foo time.Duration }
var v s
var str fmt.Stringer
str = time.Second
u := bind.BindFields(&v, bind.NamedValue("foo", &str))
u := bind.Bind(&v, bind.NamedValue("foo", &str))
if len(u) != 0 || v.Foo != time.Second {
t.Fatal()
}
@ -924,14 +950,14 @@ func TestField(t *testing.T) {
Foo int
Bar *s
}
_, u := bind.BindFieldsCreate[s](bind.NamedValue("foo", 42))
_, u := bind.CreateAndBind[s](bind.NamedValue("foo", 42))
if len(u) != 1 {
t.Fatal()
}
})
t.Run("type does not accept fields", func(t *testing.T) {
_, u := bind.BindFieldsCreate[[]int](bind.NamedValue("foo", 42))
_, u := bind.CreateAndBind[[]int](bind.NamedValue("foo", 42))
if len(u) != 1 {
t.Fatal()
}
@ -939,7 +965,7 @@ func TestField(t *testing.T) {
t.Run("zero value when no fields were bound", func(t *testing.T) {
type s struct{ Foo int }
v, u := bind.BindFieldsCreate[*s](bind.NamedValue("bar", 42))
v, u := bind.CreateAndBind[*s](bind.NamedValue("bar", 42))
if len(u) != 1 || v != nil {
t.Fatal()
}
@ -947,17 +973,26 @@ func TestField(t *testing.T) {
t.Run("create receiver", func(t *testing.T) {
type s struct{ Foo int }
v, u := bind.BindFieldsCreate[*s](bind.NamedValue("foo", 42))
v, u := bind.CreateAndBind[*s](bind.NamedValue("foo", 42))
if len(u) != 0 || v == nil || v.Foo != 42 {
t.Fatal()
}
})
t.Run("any type", func(t *testing.T) {
_, u := bind.BindFieldsCreate[any](bind.NamedValue("foo", 42))
_, u := bind.CreateAndBind[any](bind.NamedValue("foo", 42))
if len(u) != 1 {
t.Fatal()
}
})
})
t.Run("bind fields create with reflection type", func(t *testing.T) {
type s struct{ Foo int }
typ := reflect.TypeFor[s]()
v, u := bind.CreateAndBindFor(typ, bind.NamedValue("foo", 42))
if len(u) != 0 || v.Interface().(s).Foo != 42 {
t.Fatal()
}
})
}

38
lib.go
View File

@ -49,13 +49,13 @@ func BindScalar(receiver any, value ...any) bool {
}
// BindScalarCreate is like BindScalar, but it allocates the receiver from the type T.
func BindScalarCreate[T any](value ...any) (T, bool) {
func CreateAndBindScalar[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
func CreateAndBindScalarFor(t reflect.Type, value ...any) (reflect.Value, bool) {
return bindScalarCreate(t, value)
}
// ValueByPath defines a field for input to BindFields or BindFieldsCreate. It defines the field by its exact
@ -119,7 +119,7 @@ func Fields[T any]() []Field {
// Fields of is like Fields but uses type t as the input.
func FieldsOf(t reflect.Type) []Field {
return nil
return fieldsOf(t)
}
// FieldValues returns the fields of a structure value recursively. It traverses through pointers, slices and
@ -128,21 +128,21 @@ 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)
// 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)
}
// BindFieldsCreate is like BindFields, but it allocates the receiver from type T.
func BindFieldsCreate[T any](values ...Field) (T, []Field) {
return bindFieldsCreateReflect[T](values)
// CreateAndBind is like Bind, but it allocates the receiver from type T.
func CreateAndBind[T any](value ...Field) (T, []Field) {
return bindFieldsCreateReflect[T](value)
}
// 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
// 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 BindScalarCreate or the values of the type with BindScalar.
@ -152,7 +152,7 @@ func AcceptsScalar[T any]() bool {
// TypeAcceptsScalar is like AcceptsScalar, but uses type t as input.
func TypeAcceptsScalar(t reflect.Type) bool {
return false
return acceptsScalarChecked(t)
}
// AcceptsFields checks if a type can be used with BindFieldsCreate or the values of the type with BindFields.
@ -162,7 +162,7 @@ func AcceptsFields[T any]() bool {
// TypeAcceptsFields is like AcceptsFields, but uses type t as input.
func TypeAcceptsFields(t reflect.Type) bool {
return false
return acceptsFieldsChecked(t)
}
// AcceptsList checks if a type can be used to bind multiple values.
@ -172,7 +172,7 @@ func AcceptsList[T any]() bool {
// TypeAcceptsList is like AcceptsList, but uses type t as input.
func TypeAcceptsList(t reflect.Type) bool {
return false
return acceptsListChecked(t)
}
// Bindable is the same as AcceptsScalar[T]() || AcceptsFields[T]().
@ -182,5 +182,5 @@ func Bindable[T any]() bool {
// BindableType is like Bindable, but uses type t as input.
func BindableType(t reflect.Type) bool {
return false
return bindableChecked(t)
}

View File

@ -1,15 +1,15 @@
package bind_test
import (
"testing"
"code.squareroundforest.org/arpio/bind"
"testing"
)
func TestLib(t *testing.T) {
t.Run("field", func(t *testing.T) {
t.Run("type", func(t *testing.T) {
t.Run("not from input", func(t *testing.T) {
v := struct{Foo int}{42}
v := struct{ Foo int }{42}
f := bind.FieldValues(v)
if len(f) != 1 || f[0].Type() != bind.Int {
t.Fatal()

View File

@ -56,6 +56,16 @@ func bindScalarCreate(t reflect.Type, values []any) (reflect.Value, bool) {
return reflect.Zero(t), false
}
if hasCircularType(t) {
return reflect.Zero(t), false
}
for _, vi := range values {
if hasCircularReference(reflect.ValueOf(vi)) {
return reflect.Zero(t), false
}
}
if !acceptsScalar(t) {
return reflect.Zero(t), false
}
@ -89,18 +99,6 @@ func bindScalarReflect(receiver any, values []any) bool {
func bindScalarCreateReflect[T any](values []any) (T, bool) {
t := reflect.TypeFor[T]()
if hasCircularType(t) {
var tt T
return tt, false
}
for _, vi := range values {
if hasCircularReference(reflect.ValueOf(vi)) {
var tt T
return tt, false
}
}
v, ok := bindScalarCreate(t, values)
if !ok {
var tt T

View File

@ -2,6 +2,7 @@ package bind_test
import (
"code.squareroundforest.org/arpio/bind"
"reflect"
"slices"
"testing"
)
@ -181,62 +182,62 @@ func TestScalar(t *testing.T) {
t.Run("bind scalar with create", func(t *testing.T) {
t.Run("no value", func(t *testing.T) {
if _, ok := bind.BindScalarCreate[int](); ok {
if _, ok := bind.CreateAndBindScalar[int](); ok {
t.Fatal()
}
})
t.Run("does not accept scalar", func(t *testing.T) {
if _, ok := bind.BindScalarCreate[struct{ Foo int }]("42"); ok {
if _, ok := bind.CreateAndBindScalar[struct{ Foo int }]("42"); ok {
t.Fatal()
}
})
t.Run("empty interface", func(t *testing.T) {
if v, ok := bind.BindScalarCreate[any]("42"); !ok || v != "42" {
if v, ok := bind.CreateAndBindScalar[any]("42"); !ok || v != "42" {
t.Fatal()
}
})
t.Run("scalar", func(t *testing.T) {
if v, ok := bind.BindScalarCreate[int]("42"); !ok || v != 42 {
if v, ok := bind.CreateAndBindScalar[int]("42"); !ok || v != 42 {
t.Fatal()
}
})
t.Run("slice", func(t *testing.T) {
if v, ok := bind.BindScalarCreate[[]int]("21", "42", "84"); !ok || !slices.Equal(v, []int{21, 42, 84}) {
if v, ok := bind.CreateAndBindScalar[[]int]("21", "42", "84"); !ok || !slices.Equal(v, []int{21, 42, 84}) {
t.Fatal()
}
})
t.Run("slice with non-scalar type", func(t *testing.T) {
if _, ok := bind.BindScalarCreate[[]func(int)]("21", "42", "84"); ok {
if _, ok := bind.CreateAndBindScalar[[]func(int)]("21", "42", "84"); ok {
t.Fatal()
}
})
t.Run("pointer to non-scalar", func(t *testing.T) {
if _, ok := bind.BindScalarCreate[*func()]("42"); ok {
if _, ok := bind.CreateAndBindScalar[*func()]("42"); ok {
t.Fatal()
}
})
t.Run("pointer", func(t *testing.T) {
if v, ok := bind.BindScalarCreate[*int]("42"); !ok || *v != 42 {
if v, ok := bind.CreateAndBindScalar[*int]("42"); !ok || *v != 42 {
t.Fatal()
}
})
t.Run("unscannable", func(t *testing.T) {
if _, ok := bind.BindScalarCreate[int]("foo"); ok {
if _, ok := bind.CreateAndBindScalar[int]("foo"); ok {
t.Fatal()
}
})
t.Run("receiver has circular type", func(t *testing.T) {
type s []s
if _, ok := bind.BindScalarCreate[s]("foo"); ok {
if _, ok := bind.CreateAndBindScalar[s]("foo"); ok {
t.Fatal()
}
})
@ -245,9 +246,16 @@ func TestScalar(t *testing.T) {
var v any
p := &v
*p = p
if _, ok := bind.BindScalarCreate[any](p); ok {
if _, ok := bind.CreateAndBindScalar[any](p); ok {
t.Fatal()
}
})
})
t.Run("bind with reflection type", func(t *testing.T) {
v, ok := bind.CreateAndBindScalarFor(reflect.TypeFor[int](), 42)
if !ok || v.Interface() != 42 {
t.Fatal()
}
})
}

View File

@ -2,6 +2,7 @@ package bind_test
import (
"code.squareroundforest.org/arpio/bind"
"reflect"
"testing"
)
@ -274,6 +275,12 @@ func TestTypeChecks(t *testing.T) {
t.Fatal()
}
})
t.Run("with reflection type", func(t *testing.T) {
if !bind.BindableType(reflect.TypeFor[int]()) {
t.Fatal()
}
})
})
t.Run("circular type", func(t *testing.T) {
@ -378,11 +385,17 @@ func TestTypeChecks(t *testing.T) {
t.Fatal()
}
})
t.Run("with reflection type", func(t *testing.T) {
if !bind.TypeAcceptsScalar(reflect.TypeFor[int]()) {
t.Fatal()
}
})
})
t.Run("fields", func(t *testing.T) {
t.Run("yes", func(t *testing.T) {
type s struct{Foo int}
type s struct{ Foo int }
if !bind.AcceptsFields[s]() {
t.Fatal()
}
@ -400,6 +413,12 @@ func TestTypeChecks(t *testing.T) {
t.Fatal()
}
})
t.Run("with reflection type", func(t *testing.T) {
if !bind.TypeAcceptsFields(reflect.TypeFor[struct{ Foo int }]()) {
t.Fatal()
}
})
})
t.Run("list", func(t *testing.T) {
@ -421,6 +440,12 @@ func TestTypeChecks(t *testing.T) {
t.Fatal()
}
})
t.Run("with reflection type", func(t *testing.T) {
if !bind.TypeAcceptsList(reflect.TypeFor[[]int]()) {
t.Fatal()
}
})
})
})
}