From de7058d69ba59e7f1fcae56b493682e056269b3c Mon Sep 17 00:00:00 2001 From: Thang Bui Date: Tue, 12 Oct 2021 08:41:13 +0700 Subject: [PATCH 1/3] Support for map of struct --- README.md | 8 +++-- defaults.go | 20 ++++++++++++ defaults_test.go | 81 +++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 103 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 975f6d0..de1ca3d 100644 --- a/README.md +++ b/README.md @@ -37,9 +37,13 @@ type Sample struct { Slice []string `default:"[]"` SliceByJSON []int `default:"[1, 2, 3]"` // Supports JSON - Map map[string]int `default:"{}"` - MapByJSON map[string]int `default:"{\"foo\": 123}"` + Map map[string]int `default:"{}"` + MapByJSON map[string]int `default:"{\"foo\": 123}"` + MapOfStruct map[string]OtherStruct + MapOfPtrStruct map[string]*OtherStruct + MapOfStructWithTag map[string]OtherStruct `default:"{\"Key1\": {\"Foo\":123}}"` + Struct OtherStruct `default:"{}"` StructPtr *OtherStruct `default:"{\"Foo\": 123}"` diff --git a/defaults.go b/defaults.go index d4baf0f..9e9e572 100644 --- a/defaults.go +++ b/defaults.go @@ -168,6 +168,24 @@ func setField(field reflect.Value, defaultVal string) error { return err } } + case reflect.Map: + for _, e := range field.MapKeys() { + var ele interface{} + switch e.Kind() { + case reflect.Ptr: + ele = field.MapIndex(e).Elem().Interface() + default: + ele = field.MapIndex(e).Interface() + } + p := reflect.New(reflect.TypeOf(ele)) + p.Elem().Set(reflect.ValueOf(ele)) + err := Set(p.Interface()) + if err == nil || err == errInvalidType { + field.SetMapIndex(e, reflect.ValueOf(p.Elem().Interface())) + } else { + return err + } + } } return nil @@ -187,6 +205,8 @@ func shouldInitializeField(field reflect.Value, tag string) bool { } case reflect.Slice: return field.Len() > 0 || tag != "" + case reflect.Map: + return field.Len() > 0 || tag != "" } return tag != "" diff --git a/defaults_test.go b/defaults_test.go index 90f185d..5772551 100644 --- a/defaults_test.go +++ b/defaults_test.go @@ -112,10 +112,13 @@ type Sample struct { MyMap MyMap `default:"{}"` MySlice MySlice `default:"[]"` - StructWithJSON Struct `default:"{\"Foo\": 123}"` - StructPtrWithJSON *Struct `default:"{\"Foo\": 123}"` - MapWithJSON map[string]int `default:"{\"foo\": 123}"` - SliceWithJSON []string `default:"[\"foo\"]"` + StructWithJSON Struct `default:"{\"Foo\": 123}"` + StructPtrWithJSON *Struct `default:"{\"Foo\": 123}"` + MapWithJSON map[string]int `default:"{\"foo\": 123}"` + MapOfStruct map[string]Struct + MapOfPtrStruct map[string]*Struct + MapOfStructWithTag map[string]Struct `default:"{\"Struct3\": {\"Foo\":123}}"` + SliceWithJSON []string `default:"[\"foo\"]"` Empty string `default:""` @@ -203,6 +206,13 @@ func TestInit(t *testing.T) { NonInitialStruct: Struct{Foo: 123}, NonInitialStructPtr: &Struct{Foo: 123}, DeepSliceOfStructWithNoTag: [][][]Struct{{{{Foo: 123}}}}, + MapOfStruct: map[string]Struct{ + "Struct1": {Foo: 1}, + }, + MapOfPtrStruct: map[string]*Struct{ + "Struct1": {Foo: 1}, + "Struct2": {Bar: 5}, + }, } if err := Set(sample); err != nil { @@ -560,6 +570,69 @@ func TestInit(t *testing.T) { } }) + t.Run("map of struct", func(t *testing.T) { + if sample.MapOfStruct == nil { + t.Errorf("it should not unset an initiated map") + } + if len(sample.MapOfStruct) != 1 { + t.Errorf("it should not override an initiated map") + } + if sample.MapOfStruct["Struct1"].Foo != 1 { + t.Errorf("it should not override Foo field in Struct1 item") + } + if sample.MapOfStruct["Struct1"].Bar != 456 { + t.Errorf("it should set default for Bar field in Struct1 item") + } + if sample.MapOfStruct["Struct1"].WithDefault != "foo" { + t.Errorf("it should set default for WithDefault field in Struct1 item") + } + }) + + t.Run("map of ptr struct", func(t *testing.T) { + if sample.MapOfPtrStruct == nil { + t.Errorf("it should not unset an initiated map") + } + if len(sample.MapOfPtrStruct) != 2 { + t.Errorf("it should not override an initiated map") + } + if sample.MapOfPtrStruct["Struct1"].Foo != 1 { + t.Errorf("it should not override Foo field in Struct1 item") + } + if sample.MapOfPtrStruct["Struct1"].Bar != 456 { + t.Errorf("it should set default for Bar field in Struct1 item") + } + if sample.MapOfPtrStruct["Struct1"].WithDefault != "foo" { + t.Errorf("it should set default for WithDefault field in Struct1 item") + } + if sample.MapOfPtrStruct["Struct2"].Foo != 1 { + t.Errorf("it should not override Foo field in Struct2 item") + } + if sample.MapOfPtrStruct["Struct2"].Bar != 5 { + t.Errorf("it should not set default for Bar field in a Struct2 item") + } + if sample.MapOfPtrStruct["Struct2"].WithDefault != "foo" { + t.Errorf("it should set default for WithDefault field in Struct2 item") + } + }) + + t.Run("map of struct with tag", func(t *testing.T) { + if sample.MapOfStructWithTag == nil { + t.Errorf("it should set default") + } + if len(sample.MapOfStructWithTag) != 1 { + t.Errorf("it should set default with correct value") + } + if sample.MapOfStructWithTag["Struct3"].Foo != 123 { + t.Errorf("it should set default with correct value (Foo)") + } + if sample.MapOfStructWithTag["Struct1"].Bar != 0 { + t.Errorf("it should set default with correct value (Bar)") + } + if sample.MapOfStructWithTag["Struct1"].WithDefault != "" { + t.Errorf("it should set default with correct value (WithDefault)") + } + }) + t.Run("opt-out", func(t *testing.T) { if sample.NoDefault != nil { t.Errorf("it should not be set") From 858ba2051b22212abc79d5501fdbc58fd333c1f8 Mon Sep 17 00:00:00 2001 From: Thang Bui Date: Tue, 2 Nov 2021 23:31:27 +0700 Subject: [PATCH 2/3] Fix override value for a map item --- defaults.go | 23 +++++++++++++++-------- defaults_test.go | 23 +++++++++++++---------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/defaults.go b/defaults.go index 9e9e572..61856e7 100644 --- a/defaults.go +++ b/defaults.go @@ -170,18 +170,25 @@ func setField(field reflect.Value, defaultVal string) error { } case reflect.Map: for _, e := range field.MapKeys() { - var ele interface{} - switch e.Kind() { + var v = field.MapIndex(e) + var baseVal interface{} + switch v.Kind() { case reflect.Ptr: - ele = field.MapIndex(e).Elem().Interface() + baseVal = v.Elem().Interface() default: - ele = field.MapIndex(e).Interface() + baseVal = v.Interface() } - p := reflect.New(reflect.TypeOf(ele)) - p.Elem().Set(reflect.ValueOf(ele)) - err := Set(p.Interface()) + ref := reflect.New(reflect.TypeOf(baseVal)) + ref.Elem().Set(reflect.ValueOf(baseVal)) + err := Set(ref.Interface()) if err == nil || err == errInvalidType { - field.SetMapIndex(e, reflect.ValueOf(p.Elem().Interface())) + var newVal reflect.Value + if v.Kind() == reflect.Ptr { + newVal = reflect.ValueOf(ref.Interface()) + } else { + newVal = reflect.ValueOf(ref.Elem().Interface()) + } + field.SetMapIndex(e, newVal) } else { return err } diff --git a/defaults_test.go b/defaults_test.go index 5772551..d1736f2 100644 --- a/defaults_test.go +++ b/defaults_test.go @@ -29,6 +29,11 @@ type ( ) type Sample struct { + MapOfPtrStruct map[string]*Struct + MapOfStruct map[string]Struct + + MapOfStructWithTag map[string]Struct `default:"{\"Struct3\": {\"Foo\":123}}"` + Int int `default:"1"` Int8 int8 `default:"8"` Int16 int16 `default:"16"` @@ -112,13 +117,11 @@ type Sample struct { MyMap MyMap `default:"{}"` MySlice MySlice `default:"[]"` - StructWithJSON Struct `default:"{\"Foo\": 123}"` - StructPtrWithJSON *Struct `default:"{\"Foo\": 123}"` - MapWithJSON map[string]int `default:"{\"foo\": 123}"` - MapOfStruct map[string]Struct - MapOfPtrStruct map[string]*Struct - MapOfStructWithTag map[string]Struct `default:"{\"Struct3\": {\"Foo\":123}}"` - SliceWithJSON []string `default:"[\"foo\"]"` + StructWithJSON Struct `default:"{\"Foo\": 123}"` + StructPtrWithJSON *Struct `default:"{\"Foo\": 123}"` + MapWithJSON map[string]int `default:"{\"foo\": 123}"` + + SliceWithJSON []string `default:"[\"foo\"]"` Empty string `default:""` @@ -604,11 +607,11 @@ func TestInit(t *testing.T) { if sample.MapOfPtrStruct["Struct1"].WithDefault != "foo" { t.Errorf("it should set default for WithDefault field in Struct1 item") } - if sample.MapOfPtrStruct["Struct2"].Foo != 1 { + if sample.MapOfPtrStruct["Struct2"].Foo != 0 { t.Errorf("it should not override Foo field in Struct2 item") } - if sample.MapOfPtrStruct["Struct2"].Bar != 5 { - t.Errorf("it should not set default for Bar field in a Struct2 item") + if sample.MapOfPtrStruct["Struct2"].Bar != 456 { + t.Errorf("it should using setter to set default for Bar field in a Struct2 item") } if sample.MapOfPtrStruct["Struct2"].WithDefault != "foo" { t.Errorf("it should set default for WithDefault field in Struct2 item") From f4373b2684a0691b107f92733b0604a1f650e6f1 Mon Sep 17 00:00:00 2001 From: Thang Bui Date: Tue, 2 Nov 2021 23:36:47 +0700 Subject: [PATCH 3/3] Refactor test --- defaults_test.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/defaults_test.go b/defaults_test.go index d1736f2..5bfba7b 100644 --- a/defaults_test.go +++ b/defaults_test.go @@ -29,11 +29,6 @@ type ( ) type Sample struct { - MapOfPtrStruct map[string]*Struct - MapOfStruct map[string]Struct - - MapOfStructWithTag map[string]Struct `default:"{\"Struct3\": {\"Foo\":123}}"` - Int int `default:"1"` Int8 int8 `default:"8"` Int16 int16 `default:"16"` @@ -121,6 +116,10 @@ type Sample struct { StructPtrWithJSON *Struct `default:"{\"Foo\": 123}"` MapWithJSON map[string]int `default:"{\"foo\": 123}"` + MapOfPtrStruct map[string]*Struct + MapOfStruct map[string]Struct + MapOfStructWithTag map[string]Struct `default:"{\"Struct3\": {\"Foo\":123}}"` + SliceWithJSON []string `default:"[\"foo\"]"` Empty string `default:""`