diff --git a/core/conf/config_test.go b/core/conf/config_test.go index 72392511..a0d15140 100644 --- a/core/conf/config_test.go +++ b/core/conf/config_test.go @@ -123,6 +123,24 @@ d = "abcd" } } +func TestConfigWithLower(t *testing.T) { + text := `a = "foo" +b = 1 +` + tmpfile, err := createTempFile(".toml", text) + assert.Nil(t, err) + defer os.Remove(tmpfile) + + var val struct { + A string `json:"a"` + b int + } + if assert.NoError(t, Load(tmpfile, &val)) { + assert.Equal(t, "foo", val.A) + assert.Equal(t, 0, val.b) + } +} + func TestConfigJsonCanonical(t *testing.T) { text := []byte(`{"a": "foo", "B": "bar"}`) diff --git a/core/mapping/unmarshaler.go b/core/mapping/unmarshaler.go index d19469f3..e64a7675 100644 --- a/core/mapping/unmarshaler.go +++ b/core/mapping/unmarshaler.go @@ -695,6 +695,10 @@ func (u *Unmarshaler) processFieldWithEnvValue(fieldType reflect.Type, value ref func (u *Unmarshaler) processNamedField(field reflect.StructField, value reflect.Value, m valuerWithParent, fullName string) error { + if !field.IsExported() { + return nil + } + key, opts, err := u.parseOptionsWithContext(field, m, fullName) if err != nil { return err @@ -869,12 +873,9 @@ func (u *Unmarshaler) unmarshalWithFullName(m valuerWithParent, v any, fullName numFields := baseType.NumField() for i := 0; i < numFields; i++ { - field := baseType.Field(i) - if !field.IsExported() { - continue - } - - if err := u.processField(field, valElem.Field(i), m, fullName); err != nil { + typeField := baseType.Field(i) + valueField := valElem.Field(i) + if err := u.processField(typeField, valueField, m, fullName); err != nil { return err } } diff --git a/core/mapping/unmarshaler_test.go b/core/mapping/unmarshaler_test.go index 4b6148a2..1f723263 100644 --- a/core/mapping/unmarshaler_test.go +++ b/core/mapping/unmarshaler_test.go @@ -53,6 +53,52 @@ func TestUnmarshalWithoutTagName(t *testing.T) { } } +func TestUnmarshalWithLowerField(t *testing.T) { + type ( + Lower struct { + value int `key:"lower"` + } + + inner struct { + Lower + Optional bool `key:",optional"` + } + ) + m := map[string]any{ + "Optional": true, + "lower": 1, + } + + var in inner + if assert.NoError(t, UnmarshalKey(m, &in)) { + assert.True(t, in.Optional) + assert.Equal(t, 0, in.value) + } +} + +func TestUnmarshalWithLowerAnonymousStruct(t *testing.T) { + type ( + lower struct { + Value int `key:"lower"` + } + + inner struct { + lower + Optional bool `key:",optional"` + } + ) + m := map[string]any{ + "Optional": true, + "lower": 1, + } + + var in inner + if assert.NoError(t, UnmarshalKey(m, &in)) { + assert.True(t, in.Optional) + assert.Equal(t, 1, in.Value) + } +} + func TestUnmarshalWithoutTagNameWithCanonicalKey(t *testing.T) { type inner struct { Name string `key:"name"`