Skip to content

Commit

Permalink
feat: structs - InitDefaults() can init a struct by default tag
Browse files Browse the repository at this point in the history
  • Loading branch information
inhere committed Aug 27, 2022
1 parent a3ac95f commit a83c4b2
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 49 deletions.
96 changes: 77 additions & 19 deletions structs/structs.go
Original file line number Diff line number Diff line change
@@ -1,28 +1,86 @@
package structs

import "github.com/gookit/goutil/internal/comfunc"
import (
"errors"
"reflect"

// ToMap simple convert structs to map by reflect
func ToMap(st interface{}) map[string]interface{} {
mp, _ := comfunc.TryStructToMap(st)
return mp
}
"github.com/gookit/goutil/reflects"
)

// MapStruct simple copy src struct value to dst struct
// func MapStruct(srcSt, dstSt interface{}) {
// // TODO
// }

// TryToMap simple convert structs to map by reflect
func TryToMap(st interface{}) (map[string]interface{}, error) {
return comfunc.TryStructToMap(st)
const defaultInitTag = "default"

// InitOptions struct
type InitOptions struct {
TagName string
}

// MustToMap alis of TryToMap, but will panic on error
func MustToMap(st interface{}) map[string]interface{} {
mp, err := comfunc.TryStructToMap(st)
if err != nil {
panic(err)
// InitDefaults init struct default value by field "default" tag.
//
// Example:
//
// type User1 struct {
// Name string `default:"inhere"`
// Age int32 `default:"30"`
// }
//
// u1 := &User1{}
// err = structs.InitDefaults(u1, nil)
// fmt.Printf("%+v\n", u1)
// // Output: {Name:inhere Age:30}
func InitDefaults(ptr interface{}, opt *InitOptions) error {
rv := reflect.ValueOf(ptr)
if rv.Kind() != reflect.Ptr {
return errors.New("must be provider an pointer")
}

rv = rv.Elem()
if rv.Kind() != reflect.Struct {
return errors.New("must be provider an struct")
}
return mp

if opt == nil {
opt = &InitOptions{TagName: defaultInitTag}
} else if opt.TagName == "" {
opt.TagName = defaultInitTag
}

return initDefaults(rv, opt.TagName)
}

// MapStruct simple copy src struct value to dst struct
// func MapStruct(srcSt, dstSt interface{}) {
// // TODO
// }
func initDefaults(rv reflect.Value, tagName string) error {
rt := rv.Type()

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

fv := rv.Field(i)
if fv.Kind() == reflect.Struct {
err := initDefaults(fv, tagName)
if err != nil {
return err
}
continue
}

tagVal, ok := ft.Tag.Lookup(tagName)
if ok && tagVal != "" && fv.CanSet() {
val, err := reflects.ValueByKind(tagVal, fv.Kind())
if err != nil {
return err
}

fv.Set(val)
}
}

return nil
}
52 changes: 22 additions & 30 deletions structs/structs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,36 @@ package structs_test
import (
"testing"

"github.com/gookit/goutil/dump"
"github.com/gookit/goutil/structs"
"github.com/gookit/goutil/testutil/assert"
)

func TestTryToMap(t *testing.T) {
mp, err := structs.TryToMap(nil)
assert.Empty(t, mp)
assert.NoErr(t, err)

func TestInitDefaults(t *testing.T) {
type User struct {
Name string
Age int
city string
}

u := User{
Name: "inhere",
Age: 34,
city: "somewhere",
Name string `default:"inhere"`
Age int `default:""`
city string `default:""`
}

mp, err = structs.TryToMap(u)
u := &User{}
err := structs.InitDefaults(u, nil)
assert.NoErr(t, err)
dump.P(mp)
assert.Contains(t, mp, "Name")
assert.Contains(t, mp, "Age")
assert.NotContains(t, mp, "city")

mp, err = structs.TryToMap(&u)
assert.NoErr(t, err)
dump.P(mp)
assert.Eq(t, "inhere", u.Name)
assert.Eq(t, 0, u.Age)
// dump.P(u)

type User1 struct {
Name string `default:"inhere"`
Age int32 `default:"30"`
city string `default:"val0"`
}

mp = structs.ToMap(&u)
u1 := &User1{}
err = structs.InitDefaults(u1, nil)
assert.NoErr(t, err)
dump.P(mp)

assert.Panics(t, func() {
structs.MustToMap("abc")
})
assert.Eq(t, "inhere", u1.Name)
assert.Eq(t, int32(30), u1.Age)
assert.Eq(t, "", u1.city)
// dump.P(u1)
// fmt.Printf("%+v\n", u1)
}

0 comments on commit a83c4b2

Please sign in to comment.