Skip to content

Commit c9b05ae

Browse files
authored
fix: mapping optional dep not canonicaled (#2807)
1 parent 32a59db commit c9b05ae

File tree

4 files changed

+146
-0
lines changed

4 files changed

+146
-0
lines changed

core/conf/config_test.go

+24
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,30 @@ d = "abcd!@#$112"
9797
assert.Equal(t, "abcd!@#$112", val.D)
9898
}
9999

100+
func TestConfigOptional(t *testing.T) {
101+
text := `a = "foo"
102+
b = 1
103+
c = "FOO"
104+
d = "abcd"
105+
`
106+
tmpfile, err := createTempFile(".toml", text)
107+
assert.Nil(t, err)
108+
defer os.Remove(tmpfile)
109+
110+
var val struct {
111+
A string `json:"a"`
112+
B int `json:"b,optional"`
113+
C string `json:"c,optional=B"`
114+
D string `json:"d,optional=b"`
115+
}
116+
if assert.NoError(t, Load(tmpfile, &val)) {
117+
assert.Equal(t, "foo", val.A)
118+
assert.Equal(t, 1, val.B)
119+
assert.Equal(t, "FOO", val.C)
120+
assert.Equal(t, "abcd", val.D)
121+
}
122+
}
123+
100124
func TestConfigJsonCanonical(t *testing.T) {
101125
text := []byte(`{"a": "foo", "B": "bar"}`)
102126

core/mapping/jsonunmarshaler_test.go

+10
Original file line numberDiff line numberDiff line change
@@ -930,3 +930,13 @@ func TestUnmarshalJsonArray(t *testing.T) {
930930
assert.Equal(t, "kevin", v[0].Name)
931931
assert.Equal(t, 18, v[0].Age)
932932
}
933+
934+
func TestUnmarshalJsonBytesError(t *testing.T) {
935+
var v []struct {
936+
Name string `json:"name"`
937+
Age int `json:"age"`
938+
}
939+
940+
assert.Error(t, UnmarshalJsonBytes([]byte((``)), &v))
941+
assert.Error(t, UnmarshalJsonReader(strings.NewReader(``), &v))
942+
}

core/mapping/unmarshaler.go

+20
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,26 @@ func (u *Unmarshaler) parseOptionsWithContext(field reflect.StructField, m Value
372372
return key, nil, nil
373373
}
374374

375+
if u.opts.canonicalKey != nil {
376+
key = u.opts.canonicalKey(key)
377+
378+
if len(options.OptionalDep) > 0 {
379+
// need to create a new fieldOption, because the original one is shared through cache.
380+
options = &fieldOptions{
381+
fieldOptionsWithContext: fieldOptionsWithContext{
382+
Inherit: options.Inherit,
383+
FromString: options.FromString,
384+
Optional: options.Optional,
385+
Options: options.Options,
386+
Default: options.Default,
387+
EnvVar: options.EnvVar,
388+
Range: options.Range,
389+
},
390+
OptionalDep: u.opts.canonicalKey(options.OptionalDep),
391+
}
392+
}
393+
}
394+
375395
optsWithContext, err := options.toOptionsWithContext(key, m, fullName)
376396
if err != nil {
377397
return "", nil, err

core/mapping/unmarshaler_test.go

+92
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,26 @@ func TestUnmarshalWithoutTagNameWithCanonicalKey(t *testing.T) {
7777
}
7878
}
7979

80+
func TestUnmarshalWithoutTagNameWithCanonicalKeyOptionalDep(t *testing.T) {
81+
type inner struct {
82+
FirstName string `key:",optional"`
83+
LastName string `key:",optional=FirstName"`
84+
}
85+
m := map[string]interface{}{
86+
"firstname": "go",
87+
"lastname": "zero",
88+
}
89+
90+
var in inner
91+
unmarshaler := NewUnmarshaler(defaultKeyName, WithCanonicalKeyFunc(func(s string) string {
92+
return strings.ToLower(s)
93+
}))
94+
if assert.NoError(t, unmarshaler.Unmarshal(m, &in)) {
95+
assert.Equal(t, "go", in.FirstName)
96+
assert.Equal(t, "zero", in.LastName)
97+
}
98+
}
99+
80100
func TestUnmarshalBool(t *testing.T) {
81101
type inner struct {
82102
True bool `key:"yes"`
@@ -1099,6 +1119,66 @@ func TestUnmarshalStructOptionalDependsNotEnoughValue(t *testing.T) {
10991119
assert.Error(t, UnmarshalKey(m, &in))
11001120
}
11011121

1122+
func TestUnmarshalStructOptionalDependsMoreValues(t *testing.T) {
1123+
type address struct {
1124+
Optional string `key:",optional"`
1125+
OptionalDepends string `key:",optional=a=b"`
1126+
}
1127+
type inner struct {
1128+
Name string `key:"name"`
1129+
Address address `key:"address"`
1130+
}
1131+
1132+
m := map[string]interface{}{
1133+
"name": "kevin",
1134+
"address": map[string]interface{}{},
1135+
}
1136+
1137+
var in inner
1138+
assert.Error(t, UnmarshalKey(m, &in))
1139+
}
1140+
1141+
func TestUnmarshalStructMissing(t *testing.T) {
1142+
type address struct {
1143+
Optional string `key:",optional"`
1144+
OptionalDepends string `key:",optional=a=b"`
1145+
}
1146+
type inner struct {
1147+
Name string `key:"name"`
1148+
Address address `key:"address"`
1149+
}
1150+
1151+
m := map[string]interface{}{
1152+
"name": "kevin",
1153+
}
1154+
1155+
var in inner
1156+
assert.Error(t, UnmarshalKey(m, &in))
1157+
}
1158+
1159+
func TestUnmarshalNestedStructMissing(t *testing.T) {
1160+
type mostInner struct {
1161+
Name string `key:"name"`
1162+
}
1163+
type address struct {
1164+
Optional string `key:",optional"`
1165+
OptionalDepends string `key:",optional=a=b"`
1166+
MostInner mostInner
1167+
}
1168+
type inner struct {
1169+
Name string `key:"name"`
1170+
Address address `key:"address"`
1171+
}
1172+
1173+
m := map[string]interface{}{
1174+
"name": "kevin",
1175+
"address": map[string]interface{}{},
1176+
}
1177+
1178+
var in inner
1179+
assert.Error(t, UnmarshalKey(m, &in))
1180+
}
1181+
11021182
func TestUnmarshalAnonymousStructOptionalDepends(t *testing.T) {
11031183
type AnonAddress struct {
11041184
City string `key:"city"`
@@ -1422,6 +1502,18 @@ func TestUnmarshalOptionsOptionalWrongValue(t *testing.T) {
14221502
assert.Error(t, UnmarshalKey(m, &in))
14231503
}
14241504

1505+
func TestUnmarshalOptionsMissingValues(t *testing.T) {
1506+
type inner struct {
1507+
Value string `key:"value,options"`
1508+
}
1509+
m := map[string]interface{}{
1510+
"value": "first",
1511+
}
1512+
1513+
var in inner
1514+
assert.Error(t, UnmarshalKey(m, &in))
1515+
}
1516+
14251517
func TestUnmarshalStringOptionsWithStringOptionsNotString(t *testing.T) {
14261518
type inner struct {
14271519
Value string `key:"value,options=first|second"`

0 commit comments

Comments
 (0)