Merge pull request #1225 from c9s/c9s/nested-persistence

FEATURE: support nested persistence
This commit is contained in:
c9s 2023-07-10 15:29:24 +08:00 committed by GitHub
commit f71fcdee23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 72 additions and 1 deletions

View File

@ -52,6 +52,10 @@ func IterateFields(obj interface{}, cb func(ft reflect.StructField, fv reflect.V
return nil return nil
} }
func isStructPtr(tpe reflect.Type) bool {
return tpe.Kind() == reflect.Ptr && tpe.Elem().Kind() == reflect.Struct
}
func IterateFieldsByTag(obj interface{}, tagName string, cb StructFieldIterator) error { func IterateFieldsByTag(obj interface{}, tagName string, cb StructFieldIterator) error {
sv := reflect.ValueOf(obj) sv := reflect.ValueOf(obj)
st := reflect.TypeOf(obj) st := reflect.TypeOf(obj)
@ -82,11 +86,19 @@ func IterateFieldsByTag(obj interface{}, tagName string, cb StructFieldIterator)
continue continue
} }
if isStructPtr(ft.Type) && !fv.IsNil() {
// recursive iterate the struct field
if err := IterateFieldsByTag(fv.Interface(), tagName, cb); err != nil {
return fmt.Errorf("unable to iterate struct fields over the type %v: %v", ft, err)
}
}
tag, ok := ft.Tag.Lookup(tagName) tag, ok := ft.Tag.Lookup(tagName)
if !ok { if !ok {
continue continue
} }
// call the iterator
if err := cb(tag, ft, fv); err != nil { if err := cb(tag, ft, fv); err != nil {
return err return err
} }

View File

@ -40,5 +40,64 @@ func TestIterateFields(t *testing.T) {
assert.Error(t, err) assert.Error(t, err)
}) })
}
func TestIterateFieldsByTag(t *testing.T) {
t.Run("nested", func(t *testing.T) {
var a = struct {
A int `persistence:"a"`
B int `persistence:"b"`
C *struct {
D int `persistence:"d"`
E int `persistence:"e"`
}
}{
A: 1,
B: 2,
C: &struct {
D int `persistence:"d"`
E int `persistence:"e"`
}{
D: 3,
E: 4,
},
}
collectedTags := []string{}
cnt := 0
err := IterateFieldsByTag(&a, "persistence", func(tag string, ft reflect.StructField, fv reflect.Value) error {
cnt++
collectedTags = append(collectedTags, tag)
return nil
})
assert.NoError(t, err)
assert.Equal(t, 4, cnt)
assert.Equal(t, []string{"a", "b", "d", "e"}, collectedTags)
})
t.Run("nested nil", func(t *testing.T) {
var a = struct {
A int `persistence:"a"`
B int `persistence:"b"`
C *struct {
D int `persistence:"d"`
E int `persistence:"e"`
}
}{
A: 1,
B: 2,
C: nil,
}
collectedTags := []string{}
cnt := 0
err := IterateFieldsByTag(&a, "persistence", func(tag string, ft reflect.StructField, fv reflect.Value) error {
cnt++
collectedTags = append(collectedTags, tag)
return nil
})
assert.NoError(t, err)
assert.Equal(t, 2, cnt)
assert.Equal(t, []string{"a", "b"}, collectedTags)
})
} }