diff --git a/README.md b/README.md index e8096b6..c294504 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -defaults -======== +# defaults [![CircleCI](https://circleci.com/gh/creasty/defaults/tree/master.svg?style=svg)](https://circleci.com/gh/creasty/defaults/tree/master) [![codecov](https://codecov.io/gh/creasty/defaults/branch/master/graph/badge.svg)](https://codecov.io/gh/creasty/defaults) @@ -24,10 +23,9 @@ Initialize structs with default values - Recursively initializes fields in a struct - Dynamically sets default values by [`defaults.Setter`](./setter.go) interface - Preserves non-initial values from being reset with a default value +- Set default values for all addressable structs - -Usage ------ +## 1. Usage ```go package main @@ -58,6 +56,9 @@ type Sample struct { MapOfPtrStruct map[string]*OtherStruct `default:"{\"Key3\": {\"Foo\":123}}"` MapOfStructWithTag map[string]OtherStruct `default:"{\"Key4\": {\"Foo\":123}}"` + MapOfStructWithOutTag map[string]OtherStruct + MapOfSliceWithOutTag map[string][]OtherStruct + Struct OtherStruct `default:"{\"Foo\": 123}"` StructPtr *OtherStruct `default:"{\"Foo\": 123}"` @@ -79,7 +80,16 @@ func (s *OtherStruct) SetDefaults() { } func main() { - obj := &Sample{} + obj := &Sample{ + MapOfStructWithOutTag: map[string]OtherStruct{ + "hello": {}, + }, + MapOfSliceWithOutTag: map[string][]OtherStruct{ + "hello": { + {}, + }, + }, + } if err := defaults.Set(obj); err != nil { panic(err) } @@ -89,72 +99,79 @@ func main() { panic(err) } fmt.Println(string(out)) +``` - // Output: - // { - // "Name": "John Smith", - // "Age": 27, - // "Gender": "m", - // "Working": true, - // "SliceInt": [ - // 1, - // 2, - // 3 - // ], - // "SlicePtr": [ - // 1, - // 2, - // 3 - // ], - // "SliceString": [ - // "a", - // "b" - // ], - // "MapNull": {}, - // "Map": { - // "key1": 123 - // }, - // "MapOfStruct": { - // "Key2": { - // "Hello": "world", - // "Foo": 123, - // "Random": 5577006791947779410 - // } - // }, - // "MapOfPtrStruct": { - // "Key3": { - // "Hello": "world", - // "Foo": 123, - // "Random": 8674665223082153551 - // } - // }, - // "MapOfStructWithTag": { - // "Key4": { - // "Hello": "world", - // "Foo": 123, - // "Random": 6129484611666145821 - // } - // }, - // "Struct": { - // "Hello": "world", - // "Foo": 123, - // "Random": 4037200794235010051 - // }, - // "StructPtr": { - // "Hello": "world", - // "Foo": 123, - // "Random": 3916589616287113937 - // }, - // "NoTag": { - // "Hello": "world", - // "Foo": 0, - // "Random": 6334824724549167320 - // }, - // "NoOption": { - // "Hello": "", - // "Foo": 0, - // "Random": 0 - // } - // } +output + +```json +{ + "Name": "John Smith", + "Age": 27, + "Gender": "m", + "Working": true, + "SliceInt": [1, 2, 3], + "SlicePtr": [1, 2, 3], + "SliceString": ["a", "b"], + "MapNull": {}, + "Map": { + "key1": 123 + }, + "MapOfStruct": { + "Key2": { + "Hello": "world", + "Foo": 123, + "Random": 6012378114984103869 + } + }, + "MapOfPtrStruct": { + "Key3": { + "Hello": "world", + "Foo": 123, + "Random": 2848306665585403127 + } + }, + "MapOfStructWithTag": { + "Key4": { + "Hello": "world", + "Foo": 123, + "Random": 7421038243474730390 + } + }, + "MapOfStructWithOutTag": { + "hello": { + "Hello": "world", + "Foo": 0, + "Random": 6214942057906930174 + } + }, + "MapOfSliceWithOutTag": { + "hello": [ + { + "Hello": "world", + "Foo": 0, + "Random": 5317591004601161060 + } + ] + }, + "Struct": { + "Hello": "world", + "Foo": 123, + "Random": 3365636741323893358 + }, + "StructPtr": { + "Hello": "world", + "Foo": 123, + "Random": 4271644362335896782 + }, + "NoTag": { + "Hello": "world", + "Foo": 0, + "Random": 8353712922077708401 + }, + "NoOption": { + "Hello": "", + "Foo": 0, + "Random": 0 + } } ``` diff --git a/defaults.go b/defaults.go index f453928..52ba4b3 100644 --- a/defaults.go +++ b/defaults.go @@ -175,23 +175,31 @@ func setField(field reflect.Value, defaultVal string) error { } case reflect.Map: for _, e := range field.MapKeys() { - var v = field.MapIndex(e) + v := field.MapIndex(e) - switch v.Kind() { - case reflect.Ptr: - switch v.Elem().Kind() { - case reflect.Struct, reflect.Slice, reflect.Map: - if err := setField(v.Elem(), ""); err != nil { - return err - } + originalIsPtr := v.Kind() == reflect.Ptr + if originalIsPtr { + if v.IsNil() { + continue } + v = v.Elem() + } + + switch v.Kind() { case reflect.Struct, reflect.Slice, reflect.Map: - ref := reflect.New(v.Type()) - ref.Elem().Set(v) - if err := setField(ref.Elem(), ""); err != nil { + if !v.CanAddr() { + copyValue := reflect.New(v.Type()) + copyValue.Elem().Set(v) + v = copyValue.Elem() + } + + if err := setField(v, ""); err != nil { return err } - field.SetMapIndex(e, ref.Elem().Convert(v.Type())) + + if !originalIsPtr { + field.SetMapIndex(e, v) + } } } } diff --git a/defaults_test.go b/defaults_test.go index b0dc0e7..aea55f8 100644 --- a/defaults_test.go +++ b/defaults_test.go @@ -184,7 +184,6 @@ func (j *JSONOnlyType) UnmarshalJSON(b []byte) error { } func TestMustSet(t *testing.T) { - t.Run("right way", func(t *testing.T) { defer func() { if err := recover(); err != nil { @@ -226,7 +225,6 @@ func TestMustSet(t *testing.T) { } MustSet(sample) }) - } func TestInit(t *testing.T) { @@ -769,3 +767,24 @@ func TestDefaultsSetter(t *testing.T) { t.Errorf("expected 1 for MainInt, got %d", main.MainInt) } } + +type ParentWithMapOfSliceOfChild struct { + Children map[string][]Child +} + +func TestMapOfSliceOfStruct(t *testing.T) { + p := &ParentWithMapOfSliceOfChild{ + Children: map[string][]Child{ + "group1": { + {Name: "Jim"}, + }, + }, + } + Set(p) + if p.Children["group1"][0].Age != 20 { + t.Errorf("expected age to be 20, got %d", p.Children["group1"][0].Age) + } + if p.Children["group1"][0].Name != "Jim" { + t.Errorf("expected name to be Jim, got %s", p.Children["group1"][0].Name) + } +} diff --git a/example/main.go b/example/main.go index 8388350..8c177ae 100644 --- a/example/main.go +++ b/example/main.go @@ -26,6 +26,9 @@ type Sample struct { MapOfPtrStruct map[string]*OtherStruct `default:"{\"Key3\": {\"Foo\":123}}"` MapOfStructWithTag map[string]OtherStruct `default:"{\"Key4\": {\"Foo\":123}}"` + MapOfStructWithOutTag map[string]OtherStruct + MapOfSliceWithOutTag map[string][]OtherStruct + Struct OtherStruct `default:"{\"Foo\": 123}"` StructPtr *OtherStruct `default:"{\"Foo\": 123}"` @@ -47,7 +50,16 @@ func (s *OtherStruct) SetDefaults() { } func main() { - obj := &Sample{} + obj := &Sample{ + MapOfStructWithOutTag: map[string]OtherStruct{ + "hello": {}, + }, + MapOfSliceWithOutTag: map[string][]OtherStruct{ + "hello": { + {}, + }, + }, + } if err := defaults.Set(obj); err != nil { panic(err) } @@ -57,71 +69,4 @@ func main() { panic(err) } fmt.Println(string(out)) - - // Output: - // { - // "Name": "John Smith", - // "Age": 27, - // "Gender": "m", - // "Working": true, - // "SliceInt": [ - // 1, - // 2, - // 3 - // ], - // "SlicePtr": [ - // 1, - // 2, - // 3 - // ], - // "SliceString": [ - // "a", - // "b" - // ], - // "MapNull": {}, - // "Map": { - // "key1": 123 - // }, - // "MapOfStruct": { - // "Key2": { - // "Hello": "world", - // "Foo": 123, - // "Random": 5577006791947779410 - // } - // }, - // "MapOfPtrStruct": { - // "Key3": { - // "Hello": "world", - // "Foo": 123, - // "Random": 8674665223082153551 - // } - // }, - // "MapOfStructWithTag": { - // "Key4": { - // "Hello": "world", - // "Foo": 123, - // "Random": 6129484611666145821 - // } - // }, - // "Struct": { - // "Hello": "world", - // "Foo": 123, - // "Random": 4037200794235010051 - // }, - // "StructPtr": { - // "Hello": "world", - // "Foo": 123, - // "Random": 3916589616287113937 - // }, - // "NoTag": { - // "Hello": "world", - // "Foo": 0, - // "Random": 6334824724549167320 - // }, - // "NoOption": { - // "Hello": "", - // "Foo": 0, - // "Random": 0 - // } - // } } diff --git a/example/output.json b/example/output.json new file mode 100644 index 0000000..9ce6a6e --- /dev/null +++ b/example/output.json @@ -0,0 +1,70 @@ +{ + "Name": "John Smith", + "Age": 27, + "Gender": "m", + "Working": true, + "SliceInt": [1, 2, 3], + "SlicePtr": [1, 2, 3], + "SliceString": ["a", "b"], + "MapNull": {}, + "Map": { + "key1": 123 + }, + "MapOfStruct": { + "Key2": { + "Hello": "world", + "Foo": 123, + "Random": 6012378114984103869 + } + }, + "MapOfPtrStruct": { + "Key3": { + "Hello": "world", + "Foo": 123, + "Random": 2848306665585403127 + } + }, + "MapOfStructWithTag": { + "Key4": { + "Hello": "world", + "Foo": 123, + "Random": 7421038243474730390 + } + }, + "MapOfStructWithOutTag": { + "hello": { + "Hello": "world", + "Foo": 0, + "Random": 6214942057906930174 + } + }, + "MapOfSliceWithOutTag": { + "hello": [ + { + "Hello": "world", + "Foo": 0, + "Random": 5317591004601161060 + } + ] + }, + "Struct": { + "Hello": "world", + "Foo": 123, + "Random": 3365636741323893358 + }, + "StructPtr": { + "Hello": "world", + "Foo": 123, + "Random": 4271644362335896782 + }, + "NoTag": { + "Hello": "world", + "Foo": 0, + "Random": 8353712922077708401 + }, + "NoOption": { + "Hello": "", + "Foo": 0, + "Random": 0 + } +}