package bind_test import ( "code.squareroundforest.org/arpio/bind" "reflect" "slices" "testing" ) type valuer interface { value() int } type counter interface { count() int } type countedSlice []int type valueInt int func (s countedSlice) count() int { return len(s) } func (i *valueInt) value() int { return int(*i) } func TestScalar(t *testing.T) { t.Run("bind scalar", func(t *testing.T) { t.Run("no value", func(t *testing.T) { var i int if bind.BindScalar(&i) { t.Fatal() } }) t.Run("not scalar", func(t *testing.T) { var s struct{ Foo int } if bind.BindScalar(&s, "42") { t.Fatal() } }) t.Run("cannot scan", func(t *testing.T) { var i int if bind.BindScalar(&i, "foo") { t.Fatal() } }) t.Run("receiver cannot be set", func(t *testing.T) { var i int if bind.BindScalar(i, "42") { t.Fatal() } }) t.Run("one value set", func(t *testing.T) { var i int if !bind.BindScalar(&i, "42") || i != 42 { t.Fatal(i) } }) t.Run("multiple values, not slice", func(t *testing.T) { var i int if bind.BindScalar(&i, "21", "42", "84") { t.Fatal() } }) t.Run("multiple values, small slice", func(t *testing.T) { s := make([]int, 2) if bind.BindScalar(&s, "21", "42", "84") { t.Fatal() } }) t.Run("multiple values set", func(t *testing.T) { s := make([]int, 3) if !bind.BindScalar(&s, "21", "42", "84") || !slices.Equal(s, []int{21, 42, 84}) { t.Fatal() } }) t.Run("multiple values set, larger slice", func(t *testing.T) { s := make([]int, 4) if !bind.BindScalar(&s, "21", "42", "84") || !slices.Equal(s, []int{21, 42, 84, 0}) { t.Fatal() } }) t.Run("multiple values, cannot scan", func(t *testing.T) { s := make([]int, 3) if bind.BindScalar(&s, "21", "42", "foo") { t.Fatal() } }) t.Run("set item of a slice", func(t *testing.T) { s := []int{21, 42, 84} if !bind.BindScalar(&s, "27") || !slices.Equal(s, []int{27, 42, 84}) { t.Fatal(s) } }) t.Run("interface receiver", func(t *testing.T) { var i any if !bind.BindScalar(&i, 42) || i != 42 { t.Fatal(i) } }) t.Run("interface receiver has value", func(t *testing.T) { var ( i int iface any ) i = 21 iface = &i if ok := bind.BindScalar(&iface, "42"); !ok || iface != "42" || i != 21 { t.Fatal(ok, iface) } }) t.Run("wrapped with non-empty interface", func(t *testing.T) { // this could work in theory, but fails on checking the type of the receiver // we leave the test undefiend, and checking only for no panic var iface valuer i := valueInt(21) p := &i iface = p ok := bind.BindScalar(&iface, "42") if ok != (i == 42) { t.Fatal() } }) t.Run("slice wrapped by empty interface", func(t *testing.T) { var iface any s := make([]int, 3) iface = s if ok := bind.BindScalar(&iface, "21", "42", "84"); !ok || !slices.Equal(s, []int{21, 42, 84}) { t.Fatal() } }) t.Run("value has circular reference", func(t *testing.T) { var r any var v any p := &v *p = p if bind.BindScalar(&r, p) { t.Fatal() } }) t.Run("nil value", func(t *testing.T) { var v any v = 42 if !bind.BindScalar(&v, nil) || v != nil { t.Fatal() } }) t.Run("nil value and non-nillable receiver", func(t *testing.T) { var v int if bind.BindScalar(&v, nil) { t.Fatal() } }) t.Run("nil receiver", func(t *testing.T) { if bind.BindScalar(nil, 42) { t.Fatal() } }) }) t.Run("bind scalar with create", func(t *testing.T) { t.Run("no value", func(t *testing.T) { if _, ok := bind.CreateAndBindScalar[int](); ok { t.Fatal() } }) t.Run("does not accept scalar", func(t *testing.T) { if _, ok := bind.CreateAndBindScalar[struct{ Foo int }]("42"); ok { t.Fatal() } }) t.Run("empty interface", func(t *testing.T) { if v, ok := bind.CreateAndBindScalar[any]("42"); !ok || v != "42" { t.Fatal() } }) t.Run("scalar", func(t *testing.T) { if v, ok := bind.CreateAndBindScalar[int]("42"); !ok || v != 42 { t.Fatal() } }) t.Run("slice", func(t *testing.T) { 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.CreateAndBindScalar[[]func(int)]("21", "42", "84"); ok { t.Fatal() } }) t.Run("pointer to non-scalar", func(t *testing.T) { if _, ok := bind.CreateAndBindScalar[*func()]("42"); ok { t.Fatal() } }) t.Run("pointer", func(t *testing.T) { if v, ok := bind.CreateAndBindScalar[*int]("42"); !ok || *v != 42 { t.Fatal() } }) t.Run("unscannable", func(t *testing.T) { 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.CreateAndBindScalar[s]("foo"); ok { t.Fatal() } }) t.Run("value has circular reference", func(t *testing.T) { var v any p := &v *p = p 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() } }) }