dynamic: support nested persistence

This commit is contained in:
c9s 2023-07-09 15:11:09 +08:00
parent b59b42c3fa
commit 0891859b98
No known key found for this signature in database
GPG Key ID: 7385E7E464CB0A54
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
}
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 {
sv := reflect.ValueOf(obj)
st := reflect.TypeOf(obj)
@ -82,11 +86,19 @@ func IterateFieldsByTag(obj interface{}, tagName string, cb StructFieldIterator)
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)
if !ok {
continue
}
// call the iterator
if err := cb(tag, ft, fv); err != nil {
return err
}

View File

@ -40,5 +40,64 @@ func TestIterateFields(t *testing.T) {
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)
})
}