Support time.Duration in viper.Unmarshal (#205)

* Fixes #105
This commit is contained in:
Yauhen Lazurkin 2016-09-24 02:20:44 +03:00 committed by Max Wolter
parent ed0a9674c6
commit e26d6aedc1
2 changed files with 19 additions and 11 deletions

View file

@ -624,7 +624,7 @@ func (v *Viper) UnmarshalKey(key string, rawVal interface{}) error {
// on the fields of the structure are properly set. // on the fields of the structure are properly set.
func Unmarshal(rawVal interface{}) error { return v.Unmarshal(rawVal) } func Unmarshal(rawVal interface{}) error { return v.Unmarshal(rawVal) }
func (v *Viper) Unmarshal(rawVal interface{}) error { func (v *Viper) Unmarshal(rawVal interface{}) error {
err := mapstructure.WeakDecode(v.AllSettings(), rawVal) err := decode(v.AllSettings(), defaultDecoderConfig(rawVal))
if err != nil { if err != nil {
return err return err
@ -635,16 +635,19 @@ func (v *Viper) Unmarshal(rawVal interface{}) error {
return nil return nil
} }
// A wrapper around mapstructure.Decode that mimics the WeakDecode functionality // defaultDecoderConfig returns default mapsstructure.DecoderConfig with suppot
// while erroring on non existing vals in the destination struct. // of time.Duration values
func weakDecodeExact(input, output interface{}) error { func defaultDecoderConfig(output interface{}) *mapstructure.DecoderConfig {
config := &mapstructure.DecoderConfig{ return &mapstructure.DecoderConfig{
ErrorUnused: true,
Metadata: nil, Metadata: nil,
Result: output, Result: output,
WeaklyTypedInput: true, WeaklyTypedInput: true,
DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
}
} }
// A wrapper around mapstructure.Decode that mimics the WeakDecode functionality
func decode(input interface{}, config *mapstructure.DecoderConfig) error {
decoder, err := mapstructure.NewDecoder(config) decoder, err := mapstructure.NewDecoder(config)
if err != nil { if err != nil {
return err return err
@ -655,7 +658,10 @@ func weakDecodeExact(input, output interface{}) error {
// UnmarshalExact unmarshals the config into a Struct, erroring if a field is nonexistent // UnmarshalExact unmarshals the config into a Struct, erroring if a field is nonexistent
// in the destination struct. // in the destination struct.
func (v *Viper) UnmarshalExact(rawVal interface{}) error { func (v *Viper) UnmarshalExact(rawVal interface{}) error {
err := weakDecodeExact(v.AllSettings(), rawVal) config := defaultDecoderConfig(rawVal)
config.ErrorUnused = true
err := decode(v.AllSettings(), config)
if err != nil { if err != nil {
return err return err

View file

@ -453,10 +453,12 @@ func TestRecursiveAliases(t *testing.T) {
func TestUnmarshal(t *testing.T) { func TestUnmarshal(t *testing.T) {
SetDefault("port", 1313) SetDefault("port", 1313)
Set("name", "Steve") Set("name", "Steve")
Set("duration", "1s1ms")
type config struct { type config struct {
Port int Port int
Name string Name string
Duration time.Duration
} }
var C config var C config
@ -466,14 +468,14 @@ func TestUnmarshal(t *testing.T) {
t.Fatalf("unable to decode into struct, %v", err) t.Fatalf("unable to decode into struct, %v", err)
} }
assert.Equal(t, &C, &config{Name: "Steve", Port: 1313}) assert.Equal(t, &C, &config{Name: "Steve", Port: 1313, Duration: time.Second + time.Millisecond})
Set("port", 1234) Set("port", 1234)
err = Unmarshal(&C) err = Unmarshal(&C)
if err != nil { if err != nil {
t.Fatalf("unable to decode into struct, %v", err) t.Fatalf("unable to decode into struct, %v", err)
} }
assert.Equal(t, &C, &config{Name: "Steve", Port: 1234}) assert.Equal(t, &C, &config{Name: "Steve", Port: 1234, Duration: time.Second + time.Millisecond})
} }
func TestBindPFlags(t *testing.T) { func TestBindPFlags(t *testing.T) {