Skip to content

Commit 4636f4a

Browse files
author
Alex
authored
Fix unmarshal text on missing value. (#56)
We invoke TextUnmarshaler.UnmarshalText even if a value is missing from a config, which will lead to errors like #55. We should keep a zero initialized value instead of unmarshalling an empty string.
1 parent 0715044 commit 4636f4a

File tree

4 files changed

+66
-8
lines changed

4 files changed

+66
-8
lines changed

CHANGELOG.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# Changelog
22

3-
## v1.0.1 (unreleased)
3+
## v1.0.1 (2017-08-04)
44

5-
- No changes yet
5+
- Fixed unmarshal text on missing value.
66

7-
## v1.0.0 (07-31-2017)
7+
## v1.0.0 (2017-07-31)
88

99
First stable release: no breaking changes will be made in the 1.x series.
1010

@@ -23,6 +23,6 @@ First stable release: no breaking changes will be made in the 1.x series.
2323
- **[Breaking]** Unexport NewYAMLProviderFromReader* functions.
2424
- **[Breaking]** `NewProviderGroup` returns an error.
2525

26-
## v1.0.0-rc1 (06-26-2017)
26+
## v1.0.0-rc1 (2017-06-26)
2727

2828
- **[Breaking]** `Provider` interface was trimmed down to 2 methods: `Name` and `Get`

decoder.go

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,11 @@ func jsonMap(v interface{}) interface{} {
529529
return v
530530
}
531531

532+
// Skip unmarshaling if there is no value in provider and default is empty.
533+
func shouldSkip(value *Value, def string) bool {
534+
return !value.HasValue() && def == ""
535+
}
536+
532537
// tryUnmarshallers checks if the value's type implements either one of standard
533538
// interfaces in order:
534539
// 1. `json.Unmarshaler`
@@ -548,10 +553,15 @@ func (d *decoder) tryUnmarshalers(key string, value reflect.Value, def string) (
548553
switch t := value.Addr().Interface().(type) {
549554
case json.Unmarshaler:
550555
// Skip unmarshaling if there is no value.
551-
if !v.HasValue() {
556+
if shouldSkip(&v, def) {
552557
return true, nil
553558
}
554559

560+
// Use default if a value wasn't found.
561+
if !v.HasValue() {
562+
return true, errorWithKey(t.UnmarshalJSON([]byte(def)), key)
563+
}
564+
555565
// Marshal the value first.
556566
b, err := json.Marshal(jsonMap(v.Value()))
557567
if err != nil {
@@ -561,18 +571,26 @@ func (d *decoder) tryUnmarshalers(key string, value reflect.Value, def string) (
561571
// Unmarshal corresponding json.
562572
return true, errorWithKey(t.UnmarshalJSON(b), key)
563573
case encoding.TextUnmarshaler:
564-
// check if we need to use custom defaults
574+
if shouldSkip(&v, def) {
575+
return true, nil
576+
}
577+
578+
// Use default if a value wasn't found.
565579
if v.HasValue() {
566580
def = v.String()
567581
}
568582

569583
return true, errorWithKey(t.UnmarshalText([]byte(def)), key)
570584
case yaml.Unmarshaler:
571-
// Skip unmarshaling if there is no value.
572-
if !v.HasValue() {
585+
if shouldSkip(&v, def) {
573586
return true, nil
574587
}
575588

589+
// Use default if a value wasn't found.
590+
if !v.HasValue() {
591+
return true, errorWithKey(yaml.Unmarshal([]byte(def), value.Addr().Interface()), key)
592+
}
593+
576594
b, err := yaml.Marshal(v.Value())
577595
if err != nil {
578596
return true, err

static_provider_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,3 +404,12 @@ func TestStaticProviderConstructorErrors(t *testing.T) {
404404
assert.Contains(t, err.Error(), `default is empty for "Email"`)
405405
})
406406
}
407+
408+
func TestTextUnmarshalerOnMissingValue(t *testing.T) {
409+
t.Parallel()
410+
411+
p, err := NewStaticProvider(nil)
412+
require.NoError(t, err)
413+
ds := duckTales{}
414+
require.NoError(t, p.Get(Root).Populate(&ds))
415+
}

yaml_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -921,6 +921,7 @@ func (m *unmarshallerFunc) UnmarshalText(text []byte) error {
921921

922922
func TestHappyUnmarshallerChannelFunction(t *testing.T) {
923923
t.Parallel()
924+
924925
type Chart struct {
925926
Band unmarshallerChan `default:"Beatles"`
926927
Song unmarshallerFunc `default:"back"`
@@ -1336,6 +1337,35 @@ func (y *yamlUnmarshal) UnmarshalYAML(unmarshal func(interface{}) error) error {
13361337
return nil
13371338
}
13381339

1340+
func TestPopulateUnmarshalersWithDefaultsFromTags(t *testing.T) {
1341+
t.Parallel()
1342+
1343+
t.Run("JSON", func(t *testing.T) {
1344+
var j struct {
1345+
J jsonUnmarshaller `default:"explode"`
1346+
}
1347+
1348+
p, err := NewStaticProvider(nil)
1349+
require.NoError(t, err)
1350+
1351+
err = p.Get(Root).Populate(&j)
1352+
require.Error(t, err)
1353+
assert.Contains(t, err.Error(), "boom")
1354+
})
1355+
1356+
t.Run("YAML", func(t *testing.T) {
1357+
var y struct {
1358+
Y yamlUnmarshal `default:"Fake: fake"`
1359+
}
1360+
1361+
p, err := NewStaticProvider(nil)
1362+
require.NoError(t, err)
1363+
1364+
err = p.Get(Root).Populate(&y)
1365+
require.NoError(t, err)
1366+
assert.Equal(t, yamlUnmarshal{Name: "Fake"}, y.Y)
1367+
})
1368+
}
13391369
func TestPopulateNotAppropriateTypes(t *testing.T) {
13401370
t.Parallel()
13411371

@@ -1673,6 +1703,7 @@ func TestArrayElementInDifferentPositions(t *testing.T) {
16731703
a:
16741704
- protagonist: Scrooge
16751705
pilot: LaunchpadMcQuack
1706+
- protagonist:
16761707
`))
16771708

16781709
require.NoError(t, err, "Can't create a YAML provider")

0 commit comments

Comments
 (0)