Skip to content

Commit

Permalink
✨ feat: structs.InitDefaults() support handle nested struct ptr field
Browse files Browse the repository at this point in the history
  • Loading branch information
inhere committed May 18, 2023
1 parent 777ad5f commit f8d29b2
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 10 deletions.
6 changes: 6 additions & 0 deletions structs/copy.go
@@ -0,0 +1,6 @@
package structs

// MapStruct simple copy src struct value to dst struct
// func MapStruct(srcSt, dstSt any) {
// // TODO
// }
27 changes: 21 additions & 6 deletions structs/setval.go
Expand Up @@ -66,9 +66,9 @@ func initDefaults(rv reflect.Value, opt *InitOptions) error {
rt := rv.Type()

for i := 0; i < rt.NumField(); i++ {
ft := rt.Field(i)
sf := rt.Field(i)
// skip don't exported field
if ft.Name[0] >= 'a' && ft.Name[0] <= 'z' {
if IsUnexported(sf.Name) {
continue
}

Expand All @@ -85,7 +85,22 @@ func initDefaults(rv reflect.Value, opt *InitOptions) error {
continue
}

val := ft.Tag.Get(opt.TagName)
// handle for pointer field
if fv.Kind() == reflect.Pointer {
if fv.IsNil() {
fv.Set(reflect.New(fv.Type().Elem()))
}

fv = fv.Elem()
if fv.Kind() == reflect.Struct {
if err := initDefaults(fv, opt); err != nil {
return err
}
continue
}
}

val := sf.Tag.Get(opt.TagName)
if err := initDefaultValue(fv, val, opt.ParseEnv); err != nil {
return err
}
Expand Down Expand Up @@ -139,15 +154,17 @@ type SetOptions struct {
//
// see InitDefaults()
ParseDefault bool

// DefaultValTag name. tag: default
DefaultValTag string

// ParseDefaultEnv parse env var on default tag. eg: `default:"${APP_ENV}"`
//
// default: false
ParseDefaultEnv bool
}

// SetValues set data values to struct ptr
// SetValues set values to struct ptr from map data.
//
// TIPS:
//
Expand Down Expand Up @@ -196,7 +213,6 @@ func setValues(rv reflect.Value, data map[string]any, opt *SetOptions) error {
if err != nil {
return err
}

name = info.Get("name")
}

Expand All @@ -206,7 +222,6 @@ func setValues(rv reflect.Value, data map[string]any, opt *SetOptions) error {
// set field value by default tag.
if !ok && fv.IsZero() {
defVal := ft.Tag.Get(opt.DefaultValTag)

if err := initDefaultValue(fv, defVal, opt.ParseDefaultEnv); err != nil {
return err
}
Expand Down
31 changes: 27 additions & 4 deletions structs/setval_test.go
Expand Up @@ -114,21 +114,44 @@ func TestInitDefaults_convTypeError(t *testing.T) {
// dump.P(u)
}

type ExtraDefault struct {
City string `default:"chengdu"`
Github string `default:"https://github.com/inhere"`
}

func TestInitDefaults_nestStruct(t *testing.T) {
type Extra struct {
City string `default:"chengdu"`
Github string `default:"https://github.com/inhere"`
type User struct {
Name string `default:"inhere"`
Age int `default:"30"`
Extra ExtraDefault
}

u := &User{}
err := structs.InitDefaults(u)
dump.P(u)
assert.NoErr(t, err)
assert.Eq(t, "inhere", u.Name)
assert.Eq(t, 30, u.Age)
assert.Eq(t, "chengdu", u.Extra.City)
assert.Eq(t, "https://github.com/inhere", u.Extra.Github)
}

func TestInitDefaults_nestPtrStruct(t *testing.T) {
// test for pointer struct field
type User struct {
Name string `default:"inhere"`
Age int `default:"30"`
Extra Extra
Extra *ExtraDefault
}

u := &User{}
err := structs.InitDefaults(u)
dump.P(u)
assert.NoErr(t, err)
assert.Eq(t, "inhere", u.Name)
assert.Eq(t, 30, u.Age)
assert.Eq(t, "chengdu", u.Extra.City)
assert.Eq(t, "https://github.com/inhere", u.Extra.Github)
}

func TestInitDefaults_fieldPtr(t *testing.T) {
Expand Down

0 comments on commit f8d29b2

Please sign in to comment.