-
Notifications
You must be signed in to change notification settings - Fork 48
Add call to InitDefaults when structs implement Initializer interface during Unpack. #141
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
83692dc
Add support for InitDefaults on struct during Unpack to intialize def…
blakerouse 7898c30
Add changelog entry.
blakerouse 515a694
Fix issue with supporting nested structures.
blakerouse 9c6d83f
More work on supporting InitDefaults for structs, maps, and primitives.
blakerouse 3f6325b
Add more flexible cfgSub for unpack into a struct.
blakerouse 363cd00
Update docstring for Unpack.
blakerouse 9dd3a3b
Update changelog to include map and primitive support.
blakerouse f631e8b
Update code from code review.
blakerouse 63f19b1
Remove cfgEmpty and cfgSubFlex. Do not initialize pointer fields in s…
blakerouse 538138e
Update Unpack docstring to include behaviour for fields that are poin…
blakerouse 6c7d95e
Remove lines used for debugging.
blakerouse File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| // Licensed to Elasticsearch B.V. under one or more contributor | ||
| // license agreements. See the NOTICE file distributed with | ||
| // this work for additional information regarding copyright | ||
| // ownership. Elasticsearch B.V. licenses this file to you under | ||
| // the Apache License, Version 2.0 (the "License"); you may | ||
| // not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, | ||
| // software distributed under the License is distributed on an | ||
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| // KIND, either express or implied. See the License for the | ||
| // specific language governing permissions and limitations | ||
| // under the License. | ||
|
|
||
| package ucfg | ||
|
|
||
| import ( | ||
| "reflect" | ||
| ) | ||
|
|
||
| // Initializer interface provides initialization of default values support to Unpack. | ||
| // The InitDefaults method will be executed for any type passed directly or indirectly to | ||
| // Unpack. | ||
| type Initializer interface { | ||
| InitDefaults() | ||
| } | ||
|
|
||
| func tryInitDefaults(val reflect.Value) reflect.Value { | ||
| t := val.Type() | ||
|
|
||
| var initializer Initializer | ||
| if t.Implements(iInitializer) { | ||
| initializer = val.Interface().(Initializer) | ||
| initializer.InitDefaults() | ||
| return val | ||
| } else if reflect.PtrTo(t).Implements(iInitializer) { | ||
| tmp := pointerize(reflect.PtrTo(t), t, val) | ||
| initializer = tmp.Interface().(Initializer) | ||
| initializer.InitDefaults() | ||
|
|
||
| // Return the element in the pointer so the value is set into the | ||
| // field and not a pointer to the value. | ||
| return tmp.Elem() | ||
| } | ||
| return val | ||
| } | ||
|
|
||
| func hasInitDefaults(t reflect.Type) bool { | ||
| if t.Implements(iInitializer) { | ||
| return true | ||
| } | ||
| if reflect.PtrTo(t).Implements(iInitializer) { | ||
| return true | ||
| } | ||
| return false | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,235 @@ | ||
| // Licensed to Elasticsearch B.V. under one or more contributor | ||
| // license agreements. See the NOTICE file distributed with | ||
| // this work for additional information regarding copyright | ||
| // ownership. Elasticsearch B.V. licenses this file to you under | ||
| // the Apache License, Version 2.0 (the "License"); you may | ||
| // not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, | ||
| // software distributed under the License is distributed on an | ||
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| // KIND, either express or implied. See the License for the | ||
| // specific language governing permissions and limitations | ||
| // under the License. | ||
|
|
||
| package ucfg | ||
|
|
||
| import ( | ||
| "testing" | ||
|
|
||
| "github.com/stretchr/testify/assert" | ||
| ) | ||
|
|
||
| type myIntInitializer int | ||
| type myMapInitializer map[string]string | ||
|
|
||
| func (i *myIntInitializer) InitDefaults() { | ||
| *i = myIntInitializer(3) | ||
| } | ||
|
|
||
| func (m *myMapInitializer) InitDefaults() { | ||
| (*m)["init"] = "defaults" | ||
| } | ||
|
|
||
| type structInitializer struct { | ||
| I int | ||
| J int | ||
| } | ||
|
blakerouse marked this conversation as resolved.
|
||
|
|
||
| func (s *structInitializer) InitDefaults() { | ||
| s.J = 10 | ||
| } | ||
|
|
||
| type structNoInitalizer struct { | ||
| I myIntInitializer | ||
| } | ||
|
|
||
| type nestedStructInitializer struct { | ||
| M myMapInitializer | ||
| N structInitializer | ||
| O int | ||
| P structNoInitalizer | ||
| } | ||
|
|
||
| func (n *nestedStructInitializer) InitDefaults() { | ||
| n.O = 20 | ||
|
|
||
| // overridden by InitDefaults from structInitializer | ||
| n.N.J = 15 | ||
| } | ||
|
|
||
| type ptrNestedStructInitializer struct { | ||
| M *myMapInitializer | ||
| N *structInitializer | ||
| O int | ||
| P *structNoInitalizer | ||
| } | ||
|
|
||
| func (n *ptrNestedStructInitializer) InitDefaults() { | ||
| n.O = 20 | ||
| } | ||
|
|
||
| func TestInitDefaultsPrimitive(t *testing.T) { | ||
| c, _ := NewFrom(map[string]interface{}{}) | ||
|
|
||
| // unpack S | ||
| r := &struct { | ||
| I myIntInitializer | ||
| }{} | ||
|
|
||
| err := c.Unpack(r) | ||
| assert.NoError(t, err) | ||
| assert.Equal(t, myIntInitializer(3), r.I) | ||
| } | ||
|
|
||
| func TestInitDefaultsPrimitiveSet(t *testing.T) { | ||
| c, _ := NewFrom(map[string]interface{}{ | ||
| "i": 25, | ||
| }) | ||
|
|
||
| // unpack S | ||
| r := &struct { | ||
| I myIntInitializer | ||
| }{} | ||
|
|
||
| err := c.Unpack(r) | ||
| assert.NoError(t, err) | ||
| assert.Equal(t, myIntInitializer(25), r.I) | ||
| } | ||
|
|
||
| func TestInitDefaultsMap(t *testing.T) { | ||
| c, _ := NewFrom(map[string]interface{}{}) | ||
|
|
||
| // unpack S | ||
| r := &struct { | ||
| M myMapInitializer | ||
| }{} | ||
|
|
||
| err := c.Unpack(r) | ||
| assert.NoError(t, err) | ||
| assert.Equal(t, myMapInitializer{ | ||
| "init": "defaults", | ||
| }, r.M) | ||
| } | ||
|
|
||
| func TestInitDefaultsMapUpdate(t *testing.T) { | ||
| c, _ := NewFrom(map[string]interface{}{ | ||
| "m": map[string]interface{}{ | ||
| "other": "config", | ||
| }, | ||
| }) | ||
|
|
||
| // unpack S | ||
| r := &struct { | ||
| M myMapInitializer | ||
| }{} | ||
|
|
||
| err := c.Unpack(r) | ||
| assert.NoError(t, err) | ||
| assert.Equal(t, myMapInitializer{ | ||
| "init": "defaults", | ||
| "other": "config", | ||
| }, r.M) | ||
| } | ||
|
|
||
| func TestInitDefaultsMapReplace(t *testing.T) { | ||
| c, _ := NewFrom(map[string]interface{}{ | ||
| "m": map[string]interface{}{ | ||
| "init": "replace", | ||
| "other": "config", | ||
| }, | ||
| }) | ||
|
|
||
| // unpack S | ||
| r := &struct { | ||
| M myMapInitializer | ||
| }{} | ||
|
|
||
| err := c.Unpack(r) | ||
| assert.NoError(t, err) | ||
| assert.Equal(t, myMapInitializer{ | ||
| "init": "replace", | ||
| "other": "config", | ||
| }, r.M) | ||
| } | ||
|
|
||
| func TestInitDefaultsSingle(t *testing.T) { | ||
| c, _ := NewFrom(map[string]interface{}{ | ||
| "s": map[string]interface{}{ | ||
| "i": 5, | ||
| }, | ||
| }) | ||
|
|
||
| // unpack S | ||
| r := &struct { | ||
| S structInitializer | ||
| }{} | ||
|
|
||
| err := c.Unpack(r) | ||
| assert.NoError(t, err) | ||
| assert.Equal(t, 5, r.S.I) | ||
| assert.Equal(t, 10, r.S.J) | ||
| } | ||
|
|
||
| func TestInitDefaultsNested(t *testing.T) { | ||
| c, _ := NewFrom(map[string]interface{}{ | ||
| "s": map[string]interface{}{ | ||
| "n": map[string]interface{}{ | ||
| "i": 5, | ||
| }, | ||
| }, | ||
| }) | ||
|
|
||
| // unpack S | ||
| r := &struct { | ||
| S nestedStructInitializer | ||
| }{} | ||
|
|
||
| err := c.Unpack(r) | ||
| assert.NoError(t, err) | ||
| assert.Equal(t, myMapInitializer{ | ||
| "init": "defaults", | ||
| }, r.S.M) | ||
| assert.Equal(t, 5, r.S.N.I) | ||
| assert.Equal(t, 10, r.S.N.J) | ||
| assert.Equal(t, 20, r.S.O) | ||
| assert.Equal(t, myIntInitializer(3), r.S.P.I) | ||
| } | ||
|
|
||
| func TestInitDefaultsNestedEmpty(t *testing.T) { | ||
| c, _ := NewFrom(map[string]interface{}{}) | ||
|
|
||
| // unpack S | ||
| r := &struct { | ||
| S nestedStructInitializer | ||
| }{} | ||
|
|
||
| err := c.Unpack(r) | ||
| assert.NoError(t, err) | ||
| assert.Equal(t, myMapInitializer{ | ||
| "init": "defaults", | ||
| }, r.S.M) | ||
| assert.Equal(t, 0, r.S.N.I) | ||
| assert.Equal(t, 10, r.S.N.J) | ||
| assert.Equal(t, 20, r.S.O) | ||
| assert.Equal(t, myIntInitializer(3), r.S.P.I) | ||
| } | ||
|
|
||
| func TestInitDefaultsPtrNestedEmpty(t *testing.T) { | ||
| c, _ := NewFrom(map[string]interface{}{}) | ||
|
|
||
| // unpack S | ||
| r := &struct { | ||
| S ptrNestedStructInitializer | ||
| }{} | ||
|
|
||
| err := c.Unpack(r) | ||
| assert.NoError(t, err) | ||
| assert.Nil(t, r.S.M) | ||
| assert.Nil(t, r.S.N) | ||
| assert.Nil(t, r.S.P) | ||
| assert.Equal(t, 20, r.S.O) | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.