mirror of
https://github.com/spf13/viper
synced 2025-05-06 12:17:18 +00:00
Add more intricate TypeByDefaultValue logic
In addition to testing the old code, we're now supporting more generic types. You could set the default value to a struct type, and with SetTypeByDefualtValue(true), the struct type will be filled in as expected, so long as the struct type has matching fields in the data.
This commit is contained in:
parent
651d9d916a
commit
f3638581fd
2 changed files with 107 additions and 2 deletions
48
viper.go
48
viper.go
|
@ -592,7 +592,6 @@ func (v *Viper) Get(key string) interface{} {
|
||||||
|
|
||||||
valType := val
|
valType := val
|
||||||
if v.typeByDefValue {
|
if v.typeByDefValue {
|
||||||
// TODO(bep) this branch isn't covered by a single test.
|
|
||||||
path := strings.Split(lcaseKey, v.keyDelim)
|
path := strings.Split(lcaseKey, v.keyDelim)
|
||||||
defVal := v.searchMap(v.defaults, path)
|
defVal := v.searchMap(v.defaults, path)
|
||||||
if defVal != nil {
|
if defVal != nil {
|
||||||
|
@ -615,8 +614,53 @@ func (v *Viper) Get(key string) interface{} {
|
||||||
return cast.ToDuration(val)
|
return cast.ToDuration(val)
|
||||||
case []string:
|
case []string:
|
||||||
return cast.ToStringSlice(val)
|
return cast.ToStringSlice(val)
|
||||||
|
default:
|
||||||
|
return convert(reflect.ValueOf(val), reflect.TypeOf(valType)).Interface()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func convert(v reflect.Value, typ reflect.Type) reflect.Value {
|
||||||
|
if v.Kind() == reflect.Interface {
|
||||||
|
return convert(v.Elem(), typ)
|
||||||
|
}
|
||||||
|
switch typ.Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
res := reflect.New(typ.Elem())
|
||||||
|
res.Elem().Set(convert(v, typ.Elem()))
|
||||||
|
return res
|
||||||
|
case reflect.Struct:
|
||||||
|
if v.Kind() != reflect.Map {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
res := reflect.New(typ).Elem()
|
||||||
|
for i := 0; i < typ.NumField(); i++ {
|
||||||
|
field := typ.Field(i)
|
||||||
|
fieldVal := v.MapIndex(reflect.ValueOf(field.Name))
|
||||||
|
if !fieldVal.IsValid() {
|
||||||
|
fieldVal = v.MapIndex(reflect.ValueOf(strings.ToLower(field.Name)))
|
||||||
|
if !fieldVal.IsValid() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.Field(i).Set(convert(fieldVal, field.Type))
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
case reflect.Slice, reflect.Array:
|
||||||
|
if v.Kind() != reflect.Slice {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
res := reflect.Zero(typ)
|
||||||
|
elemType := typ.Elem()
|
||||||
|
for i := 0; i < v.Len(); i++ {
|
||||||
|
res = reflect.Append(res, convert(v.Index(i), elemType))
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
default:
|
||||||
|
if !v.Type().ConvertibleTo(typ) {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return v.Convert(typ)
|
||||||
}
|
}
|
||||||
return val
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sub returns new Viper instance representing a sub tree of this instance.
|
// Sub returns new Viper instance representing a sub tree of this instance.
|
||||||
|
|
|
@ -789,6 +789,67 @@ func TestSub(t *testing.T) {
|
||||||
assert.Equal(t, (*Viper)(nil), subv)
|
assert.Equal(t, (*Viper)(nil), subv)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var yamlTypeExample = []byte(`
|
||||||
|
hobbies:
|
||||||
|
- skateboarding
|
||||||
|
- snowboarding
|
||||||
|
languages: go golang python
|
||||||
|
job:
|
||||||
|
salary: 100,000 USD
|
||||||
|
medical: full
|
||||||
|
clothes:
|
||||||
|
- jacket: leather
|
||||||
|
trousers: denim
|
||||||
|
pants:
|
||||||
|
size: large
|
||||||
|
- jacket: denim
|
||||||
|
trousers: slacks
|
||||||
|
pants:
|
||||||
|
size: medium
|
||||||
|
`)
|
||||||
|
|
||||||
|
type Clothing struct {
|
||||||
|
Jacket string
|
||||||
|
Trousers string
|
||||||
|
Pants Pants
|
||||||
|
}
|
||||||
|
|
||||||
|
type Pants struct {
|
||||||
|
Size string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Job struct {
|
||||||
|
Salary string
|
||||||
|
Medical string
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTypeByDefaultValue(t *testing.T) {
|
||||||
|
v := New()
|
||||||
|
v.SetConfigType("yaml")
|
||||||
|
v.SetDefault("hobbies", []string{})
|
||||||
|
v.SetDefault("languages", []string{})
|
||||||
|
v.SetDefault("job", &Job{})
|
||||||
|
v.SetDefault("clothes", []Clothing{})
|
||||||
|
v.SetTypeByDefaultValue(true)
|
||||||
|
err := v.ReadConfig(bytes.NewBuffer(yamlTypeExample))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, []string{"skateboarding", "snowboarding"}, v.Get("hobbies"))
|
||||||
|
assert.Equal(t, []string{"go", "golang", "python"}, v.Get("languages"))
|
||||||
|
assert.Equal(t, []Clothing{
|
||||||
|
{
|
||||||
|
Jacket: "leather",
|
||||||
|
Trousers: "denim",
|
||||||
|
Pants: Pants{Size: "large"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Jacket: "denim",
|
||||||
|
Trousers: "slacks",
|
||||||
|
Pants: Pants{Size: "medium"},
|
||||||
|
},
|
||||||
|
}, v.Get("clothes"))
|
||||||
|
}
|
||||||
|
|
||||||
var yamlMergeExampleTgt = []byte(`
|
var yamlMergeExampleTgt = []byte(`
|
||||||
hello:
|
hello:
|
||||||
pop: 37890
|
pop: 37890
|
||||||
|
|
Loading…
Add table
Reference in a new issue