diff --git a/config/config.go b/config/config.go index 3d73d69e..dd4c4b88 100644 --- a/config/config.go +++ b/config/config.go @@ -109,6 +109,10 @@ func (k *KoanfAdapter) Unmarshal(path string, o interface{}) error { Result: o, ErrorUnused: true, WeaklyTypedInput: true, + DecodeHook: mapstructure.ComposeDecodeHookFunc( + mapstructure.StringToTimeDurationHookFunc(), + stringToConfigDurationHookFunc(), + ), }, }) } @@ -239,3 +243,28 @@ func (m MapAdapter) Route(s string) contract.ConfigAccessor { panic(fmt.Sprintf("value at path %s is not a valid Router", s)) } } + +func stringToConfigDurationHookFunc() mapstructure.DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data interface{}) (interface{}, error) { + if t != reflect.TypeOf(Duration{}) { + return data, nil + } + var val string + if f.Kind() == reflect.Float64 { + val = fmt.Sprintf("%v", data) + } else if f.Kind() == reflect.String { + val = fmt.Sprintf(`"%v"`, data) + } else { + return nil, fmt.Errorf("expected a %s, should be float64 or string, got '%s'", t.String(), f.String()) + } + d := Duration{} + err := d.UnmarshalJSON([]byte(val)) + if err != nil { + return nil, err + } + return d, nil + } +} diff --git a/config/config_test.go b/config/config_test.go index 0e9263e7..8b70fbd5 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -116,6 +116,15 @@ func TestKoanfAdapter_Unmarshal(t *gotesting.T) { err := ka.Unmarshal("foo.bar", &target) assert.NoError(t, err) assert.Equal(t, "baz", target) + + var r Duration + err = ka.Unmarshal("duration_string", &r) + assert.NoError(t, err) + assert.Equal(t, r, Duration{1 * time.Second}) + + err = ka.Unmarshal("duration_number", &r) + assert.NoError(t, err) + assert.Equal(t, r, Duration{1 * time.Nanosecond}) } func TestMapAdapter_Bool(t *gotesting.T) { diff --git a/config/testdata/mock.json b/config/testdata/mock.json index fa730a3e..72873e4f 100644 --- a/config/testdata/mock.json +++ b/config/testdata/mock.json @@ -1,10 +1,12 @@ -{ - "foo": { - "bar": "baz" - }, - "bool": true, - "string": "string", - "int": 42, - "strings": ["foo", "bar"], - "float": 1.0 -} +{ + "foo": { + "bar": "baz" + }, + "bool": true, + "string": "string", + "int": 42, + "strings": ["foo", "bar"], + "float": 1.0, + "duration_string": "1s", + "duration_number": 1 +}