diff --git a/reflect/fields.go b/reflect/fields.go index 73e593c2f..a220de448 100644 --- a/reflect/fields.go +++ b/reflect/fields.go @@ -1,6 +1,7 @@ package reflect import ( + "fmt" "reflect" "time" @@ -92,6 +93,18 @@ func NameOf(m any) (name string) { // - db.Omit() // - db.Select() func HasFields(m any, in ...string) (out []string, err error) { + defer func() { + p := recover() + if p != nil { + if pe, cast := p.(error); cast { + err = pe + } else { + err = fmt.Errorf( + "(paniced) failed: %#v", + p) + } + } + }() mp := make(map[string]any) var inspect func(r any) inspect = func(r any) { @@ -109,6 +122,10 @@ func HasFields(m any, in ...string) (out []string, err error) { } switch fv.Kind() { case reflect.Ptr: + if ft.Anonymous { + inspect(fv.Interface()) + continue + } inst := fv.Interface() mp[ft.Name] = inst case reflect.Struct: diff --git a/reflect/reflect_test.go b/reflect/reflect_test.go new file mode 100644 index 000000000..7b2431042 --- /dev/null +++ b/reflect/reflect_test.go @@ -0,0 +1,72 @@ +package reflect + +import ( + "errors" + "testing" + + "github.com/onsi/gomega" +) + +func TestHasField(t *testing.T) { + g := gomega.NewGomegaWithT(t) + type B struct { + Name string + Age string + } + type B2 struct { + Name2 string + Age2 string + } + type M struct { + B + *B2 + Ptr *B + Object B + Int int + IntPtr *int + List []string + } + + // Test expected. + _, err := HasFields( + &M{B2: &B2{}}, + "Name", + "Age", + "Name2", + "Age2", + "Ptr", + "Object", + "Int", + "IntPtr", + "List") + g.Expect(err).To(gomega.BeNil()) + + // Test anonymous NIL pointer. + _, err = HasFields( + &M{}, // PROBLEM HERE. + "Name", + "Age", + "Name2", + "Age2", + "Ptr", + "Object", + "Int", + "IntPtr", + "List") + g.Expect(err).ToNot(gomega.BeNil()) + + // Invalid field. + _, err = HasFields( + &M{B2: &B2{}}, + "Name", + "Age", + "Name2", + "Age2", + "Ptr", + "NOT-VALID", // PROBLEM HERE + "Object", + "Int", + "IntPtr", + "List") + g.Expect(errors.Is(err, &FieldNotValid{})).To(gomega.BeTrue()) +}