diff --git a/field.go b/field.go index 4d543d3..f025043 100644 --- a/field.go +++ b/field.go @@ -445,16 +445,7 @@ func groupFields(f []Field) [][]Field { return groups } -func bindFieldsReflect(structure any, values []Field) []Field { - receiver := reflect.ValueOf(structure) - if hasCircularReference(receiver) { - return values - } - - if !acceptsFields(receiver.Type()) { - return values - } - +func bindFields(receiver reflect.Value, values []Field) []Field { unmatched, try := filterFields(fieldHasCircRef, values) groups := groupFields(try) for _, g := range groups { @@ -465,3 +456,42 @@ func bindFieldsReflect(structure any, values []Field) []Field { return unmatched } + +func bindFieldsReflect(structure any, values []Field) []Field { + receiver := reflect.ValueOf(structure) + if hasCircularReference(receiver) { + return values + } + + if !acceptsFields(receiver.Type()) { + return values + } + + return bindFields(receiver, values) +} + +func bindFieldsCreateReflect[T any](values []Field) (T, []Field) { + t := reflect.TypeFor[T]() + if hasCircularType(t) { + var r T + return r, values + } + + if !acceptsFields(t) { + var r T + return r, values + } + + receiver, ok := allocate(t, 1) + if !ok { + var r T + return r, values + } + + unmatched := bindFields(receiver, values) + if len(unmatched) == len(values) { + receiver = reflect.Zero(t) + } + + return receiver.Interface().(T), unmatched +} diff --git a/field_test.go b/field_test.go index 6f8a99b..8e5bd79 100644 --- a/field_test.go +++ b/field_test.go @@ -701,4 +701,40 @@ func TestField(t *testing.T) { } }) }) + + t.Run("bind fields create", func(t *testing.T) { + t.Run("circular type", func(t *testing.T) { + type s struct { + Foo int + Bar *s + } + _, u := bind.BindFieldsCreate[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)) + if len(u) != 1 { + t.Fatal() + } + }) + + 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)) + if len(u) != 1 || v != nil { + t.Fatal() + } + }) + + t.Run("create receiver", func(t *testing.T) { + type s struct{ Foo int } + v, u := bind.BindFieldsCreate[*s](bind.NamedValue("foo", 42)) + if len(u) != 0 || v == nil || v.Foo != 42 { + t.Fatal() + } + }) + }) } diff --git a/lib.go b/lib.go index 38f03db..d4ff89d 100644 --- a/lib.go +++ b/lib.go @@ -65,8 +65,7 @@ func BindFields(structure any, values ...Field) []Field { } func BindFieldsCreate[T any](values ...Field) (T, []Field) { - var t T - return t, nil + return bindFieldsCreateReflect[T](values) } func AcceptsScalar[T any]() bool {