From c174e2427c1891ac084814d40c9f2c64019d23f1 Mon Sep 17 00:00:00 2001 From: Kiril Zvezdarov Date: Sun, 26 Apr 2015 15:02:19 -0400 Subject: [PATCH] Added accessing deep keys by recursive hierarchical search --- viper.go | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/viper.go b/viper.go index e099839..a148209 100644 --- a/viper.go +++ b/viper.go @@ -107,6 +107,10 @@ func (rce RemoteConfigError) Error() string { // "endpoint": "https://localhost" // } type Viper struct { + // Delimiter that separates a list of keys + // used to access a nested value in one go + keyDelim string + // A set of paths to look for the config file in configPaths []string @@ -134,6 +138,7 @@ type Viper struct { // Returns an initialized Viper instance. func New() *Viper { v := new(Viper) + v.keyDelim = "." v.configName = "config" v.config = make(map[string]interface{}) v.override = make(map[string]interface{}) @@ -307,6 +312,26 @@ func (v *Viper) providerPathExists(p *remoteProvider) bool { return false } +func (v *Viper) searchMap(source map[string]interface{}, path []string) interface{} { + + if len(path) == 0 { + return source + } + + if next, ok := source[path[0]]; ok { + switch next.(type) { + case map[string]interface{}: + // Type assertion is safe here since it is only reached + // if the type of `next` is the same as the type being asserted + return v.searchMap(next.(map[string]interface{}), path[1:]) + default: + return next + } + } else { + return nil + } +} + // Viper is essentially repository for configurations // Get can retrieve any value given the key to use // Get has the behavior of returning the value associated with the first @@ -316,11 +341,19 @@ func (v *Viper) providerPathExists(p *remoteProvider) bool { // Get returns an interface. For a specific value use one of the Get____ methods. func Get(key string) interface{} { return v.Get(key) } func (v *Viper) Get(key string) interface{} { - key = strings.ToLower(key) - val := v.find(key) + path := strings.Split(key, v.keyDelim) + + val := v.find(strings.ToLower(key)) if val == nil { - return nil + source := v.find(path[0]) + if source == nil { + return nil + } + + if reflect.TypeOf(source).Kind() == reflect.Map { + val = v.searchMap(cast.ToStringMap(source), path[1:]) + } } switch val.(type) {