OSDN Git Service

Hulk did something
[bytom/vapor.git] / vendor / github.com / spf13 / viper / viper.go
diff --git a/vendor/github.com/spf13/viper/viper.go b/vendor/github.com/spf13/viper/viper.go
new file mode 100644 (file)
index 0000000..963861a
--- /dev/null
@@ -0,0 +1,1574 @@
+// Copyright © 2014 Steve Francia <spf@spf13.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+// Viper is a application configuration system.
+// It believes that applications can be configured a variety of ways
+// via flags, ENVIRONMENT variables, configuration files retrieved
+// from the file system, or a remote key/value store.
+
+// Each item takes precedence over the item below it:
+
+// overrides
+// flag
+// env
+// config
+// key/value store
+// default
+
+package viper
+
+import (
+       "bytes"
+       "encoding/csv"
+       "fmt"
+       "io"
+       "log"
+       "os"
+       "path/filepath"
+       "reflect"
+       "strings"
+       "time"
+
+       "github.com/fsnotify/fsnotify"
+       "github.com/mitchellh/mapstructure"
+       "github.com/spf13/afero"
+       "github.com/spf13/cast"
+       jww "github.com/spf13/jwalterweatherman"
+       "github.com/spf13/pflag"
+)
+
+var v *Viper
+
+type RemoteResponse struct {
+       Value []byte
+       Error error
+}
+
+func init() {
+       v = New()
+}
+
+type remoteConfigFactory interface {
+       Get(rp RemoteProvider) (io.Reader, error)
+       Watch(rp RemoteProvider) (io.Reader, error)
+       WatchChannel(rp RemoteProvider) (<-chan *RemoteResponse, chan bool)
+}
+
+// RemoteConfig is optional, see the remote package
+var RemoteConfig remoteConfigFactory
+
+// UnsupportedConfigError denotes encountering an unsupported
+// configuration filetype.
+type UnsupportedConfigError string
+
+// Error returns the formatted configuration error.
+func (str UnsupportedConfigError) Error() string {
+       return fmt.Sprintf("Unsupported Config Type %q", string(str))
+}
+
+// UnsupportedRemoteProviderError denotes encountering an unsupported remote
+// provider. Currently only etcd and Consul are supported.
+type UnsupportedRemoteProviderError string
+
+// Error returns the formatted remote provider error.
+func (str UnsupportedRemoteProviderError) Error() string {
+       return fmt.Sprintf("Unsupported Remote Provider Type %q", string(str))
+}
+
+// RemoteConfigError denotes encountering an error while trying to
+// pull the configuration from the remote provider.
+type RemoteConfigError string
+
+// Error returns the formatted remote provider error
+func (rce RemoteConfigError) Error() string {
+       return fmt.Sprintf("Remote Configurations Error: %s", string(rce))
+}
+
+// ConfigFileNotFoundError denotes failing to find configuration file.
+type ConfigFileNotFoundError struct {
+       name, locations string
+}
+
+// Error returns the formatted configuration error.
+func (fnfe ConfigFileNotFoundError) Error() string {
+       return fmt.Sprintf("Config File %q Not Found in %q", fnfe.name, fnfe.locations)
+}
+
+// Viper is a prioritized configuration registry. It
+// maintains a set of configuration sources, fetches
+// values to populate those, and provides them according
+// to the source's priority.
+// The priority of the sources is the following:
+// 1. overrides
+// 2. flags
+// 3. env. variables
+// 4. config file
+// 5. key/value store
+// 6. defaults
+//
+// For example, if values from the following sources were loaded:
+//
+//  Defaults : {
+//     "secret": "",
+//     "user": "default",
+//     "endpoint": "https://localhost"
+//  }
+//  Config : {
+//     "user": "root"
+//     "secret": "defaultsecret"
+//  }
+//  Env : {
+//     "secret": "somesecretkey"
+//  }
+//
+// The resulting config will have the following values:
+//
+//     {
+//             "secret": "somesecretkey",
+//             "user": "root",
+//             "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
+
+       // The filesystem to read config from.
+       fs afero.Fs
+
+       // A set of remote providers to search for the configuration
+       remoteProviders []*defaultRemoteProvider
+
+       // Name of file to look for inside the path
+       configName string
+       configFile string
+       configType string
+       envPrefix  string
+
+       automaticEnvApplied bool
+       envKeyReplacer      *strings.Replacer
+
+       config         map[string]interface{}
+       override       map[string]interface{}
+       defaults       map[string]interface{}
+       kvstore        map[string]interface{}
+       pflags         map[string]FlagValue
+       env            map[string]string
+       aliases        map[string]string
+       typeByDefValue bool
+
+       onConfigChange func(fsnotify.Event)
+}
+
+// New returns an initialized Viper instance.
+func New() *Viper {
+       v := new(Viper)
+       v.keyDelim = "."
+       v.configName = "config"
+       v.fs = afero.NewOsFs()
+       v.config = make(map[string]interface{})
+       v.override = make(map[string]interface{})
+       v.defaults = make(map[string]interface{})
+       v.kvstore = make(map[string]interface{})
+       v.pflags = make(map[string]FlagValue)
+       v.env = make(map[string]string)
+       v.aliases = make(map[string]string)
+       v.typeByDefValue = false
+
+       return v
+}
+
+// Intended for testing, will reset all to default settings.
+// In the public interface for the viper package so applications
+// can use it in their testing as well.
+func Reset() {
+       v = New()
+       SupportedExts = []string{"json", "toml", "yaml", "yml", "hcl"}
+       SupportedRemoteProviders = []string{"etcd", "consul"}
+}
+
+type defaultRemoteProvider struct {
+       provider      string
+       endpoint      string
+       path          string
+       secretKeyring string
+}
+
+func (rp defaultRemoteProvider) Provider() string {
+       return rp.provider
+}
+
+func (rp defaultRemoteProvider) Endpoint() string {
+       return rp.endpoint
+}
+
+func (rp defaultRemoteProvider) Path() string {
+       return rp.path
+}
+
+func (rp defaultRemoteProvider) SecretKeyring() string {
+       return rp.secretKeyring
+}
+
+// RemoteProvider stores the configuration necessary
+// to connect to a remote key/value store.
+// Optional secretKeyring to unencrypt encrypted values
+// can be provided.
+type RemoteProvider interface {
+       Provider() string
+       Endpoint() string
+       Path() string
+       SecretKeyring() string
+}
+
+// SupportedExts are universally supported extensions.
+var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl"}
+
+// SupportedRemoteProviders are universally supported remote providers.
+var SupportedRemoteProviders = []string{"etcd", "consul"}
+
+func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) }
+func (v *Viper) OnConfigChange(run func(in fsnotify.Event)) {
+       v.onConfigChange = run
+}
+
+func WatchConfig() { v.WatchConfig() }
+func (v *Viper) WatchConfig() {
+       go func() {
+               watcher, err := fsnotify.NewWatcher()
+               if err != nil {
+                       log.Fatal(err)
+               }
+               defer watcher.Close()
+
+               // we have to watch the entire directory to pick up renames/atomic saves in a cross-platform way
+               filename, err := v.getConfigFile()
+               if err != nil {
+                       log.Println("error:", err)
+                       return
+               }
+
+               configFile := filepath.Clean(filename)
+               configDir, _ := filepath.Split(configFile)
+
+               done := make(chan bool)
+               go func() {
+                       for {
+                               select {
+                               case event := <-watcher.Events:
+                                       // we only care about the config file
+                                       if filepath.Clean(event.Name) == configFile {
+                                               if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create {
+                                                       err := v.ReadInConfig()
+                                                       if err != nil {
+                                                               log.Println("error:", err)
+                                                       }
+                                                       v.onConfigChange(event)
+                                               }
+                                       }
+                               case err := <-watcher.Errors:
+                                       log.Println("error:", err)
+                               }
+                       }
+               }()
+
+               watcher.Add(configDir)
+               <-done
+       }()
+}
+
+// SetConfigFile explicitly defines the path, name and extension of the config file.
+// Viper will use this and not check any of the config paths.
+func SetConfigFile(in string) { v.SetConfigFile(in) }
+func (v *Viper) SetConfigFile(in string) {
+       if in != "" {
+               v.configFile = in
+       }
+}
+
+// SetEnvPrefix defines a prefix that ENVIRONMENT variables will use.
+// E.g. if your prefix is "spf", the env registry will look for env
+// variables that start with "SPF_".
+func SetEnvPrefix(in string) { v.SetEnvPrefix(in) }
+func (v *Viper) SetEnvPrefix(in string) {
+       if in != "" {
+               v.envPrefix = in
+       }
+}
+
+func (v *Viper) mergeWithEnvPrefix(in string) string {
+       if v.envPrefix != "" {
+               return strings.ToUpper(v.envPrefix + "_" + in)
+       }
+
+       return strings.ToUpper(in)
+}
+
+// TODO: should getEnv logic be moved into find(). Can generalize the use of
+// rewriting keys many things, Ex: Get('someKey') -> some_key
+// (camel case to snake case for JSON keys perhaps)
+
+// getEnv is a wrapper around os.Getenv which replaces characters in the original
+// key. This allows env vars which have different keys than the config object
+// keys.
+func (v *Viper) getEnv(key string) string {
+       if v.envKeyReplacer != nil {
+               key = v.envKeyReplacer.Replace(key)
+       }
+       return os.Getenv(key)
+}
+
+// ConfigFileUsed returns the file used to populate the config registry.
+func ConfigFileUsed() string            { return v.ConfigFileUsed() }
+func (v *Viper) ConfigFileUsed() string { return v.configFile }
+
+// AddConfigPath adds a path for Viper to search for the config file in.
+// Can be called multiple times to define multiple search paths.
+func AddConfigPath(in string) { v.AddConfigPath(in) }
+func (v *Viper) AddConfigPath(in string) {
+       if in != "" {
+               absin := absPathify(in)
+               jww.INFO.Println("adding", absin, "to paths to search")
+               if !stringInSlice(absin, v.configPaths) {
+                       v.configPaths = append(v.configPaths, absin)
+               }
+       }
+}
+
+// AddRemoteProvider adds a remote configuration source.
+// Remote Providers are searched in the order they are added.
+// provider is a string value, "etcd" or "consul" are currently supported.
+// endpoint is the url.  etcd requires http://ip:port  consul requires ip:port
+// path is the path in the k/v store to retrieve configuration
+// To retrieve a config file called myapp.json from /configs/myapp.json
+// you should set path to /configs and set config name (SetConfigName()) to
+// "myapp"
+func AddRemoteProvider(provider, endpoint, path string) error {
+       return v.AddRemoteProvider(provider, endpoint, path)
+}
+func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error {
+       if !stringInSlice(provider, SupportedRemoteProviders) {
+               return UnsupportedRemoteProviderError(provider)
+       }
+       if provider != "" && endpoint != "" {
+               jww.INFO.Printf("adding %s:%s to remote provider list", provider, endpoint)
+               rp := &defaultRemoteProvider{
+                       endpoint: endpoint,
+                       provider: provider,
+                       path:     path,
+               }
+               if !v.providerPathExists(rp) {
+                       v.remoteProviders = append(v.remoteProviders, rp)
+               }
+       }
+       return nil
+}
+
+// AddSecureRemoteProvider adds a remote configuration source.
+// Secure Remote Providers are searched in the order they are added.
+// provider is a string value, "etcd" or "consul" are currently supported.
+// endpoint is the url.  etcd requires http://ip:port  consul requires ip:port
+// secretkeyring is the filepath to your openpgp secret keyring.  e.g. /etc/secrets/myring.gpg
+// path is the path in the k/v store to retrieve configuration
+// To retrieve a config file called myapp.json from /configs/myapp.json
+// you should set path to /configs and set config name (SetConfigName()) to
+// "myapp"
+// Secure Remote Providers are implemented with github.com/xordataexchange/crypt
+func AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error {
+       return v.AddSecureRemoteProvider(provider, endpoint, path, secretkeyring)
+}
+
+func (v *Viper) AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error {
+       if !stringInSlice(provider, SupportedRemoteProviders) {
+               return UnsupportedRemoteProviderError(provider)
+       }
+       if provider != "" && endpoint != "" {
+               jww.INFO.Printf("adding %s:%s to remote provider list", provider, endpoint)
+               rp := &defaultRemoteProvider{
+                       endpoint:      endpoint,
+                       provider:      provider,
+                       path:          path,
+                       secretKeyring: secretkeyring,
+               }
+               if !v.providerPathExists(rp) {
+                       v.remoteProviders = append(v.remoteProviders, rp)
+               }
+       }
+       return nil
+}
+
+func (v *Viper) providerPathExists(p *defaultRemoteProvider) bool {
+       for _, y := range v.remoteProviders {
+               if reflect.DeepEqual(y, p) {
+                       return true
+               }
+       }
+       return false
+}
+
+// searchMap recursively searches for a value for path in source map.
+// Returns nil if not found.
+// Note: This assumes that the path entries and map keys are lower cased.
+func (v *Viper) searchMap(source map[string]interface{}, path []string) interface{} {
+       if len(path) == 0 {
+               return source
+       }
+
+       next, ok := source[path[0]]
+       if ok {
+               // Fast path
+               if len(path) == 1 {
+                       return next
+               }
+
+               // Nested case
+               switch next.(type) {
+               case map[interface{}]interface{}:
+                       return v.searchMap(cast.ToStringMap(next), path[1:])
+               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:
+                       // got a value but nested key expected, return "nil" for not found
+                       return nil
+               }
+       }
+       return nil
+}
+
+// searchMapWithPathPrefixes recursively searches for a value for path in source map.
+//
+// While searchMap() considers each path element as a single map key, this
+// function searches for, and prioritizes, merged path elements.
+// e.g., if in the source, "foo" is defined with a sub-key "bar", and "foo.bar"
+// is also defined, this latter value is returned for path ["foo", "bar"].
+//
+// This should be useful only at config level (other maps may not contain dots
+// in their keys).
+//
+// Note: This assumes that the path entries and map keys are lower cased.
+func (v *Viper) searchMapWithPathPrefixes(source map[string]interface{}, path []string) interface{} {
+       if len(path) == 0 {
+               return source
+       }
+
+       // search for path prefixes, starting from the longest one
+       for i := len(path); i > 0; i-- {
+               prefixKey := strings.ToLower(strings.Join(path[0:i], v.keyDelim))
+
+               next, ok := source[prefixKey]
+               if ok {
+                       // Fast path
+                       if i == len(path) {
+                               return next
+                       }
+
+                       // Nested case
+                       var val interface{}
+                       switch next.(type) {
+                       case map[interface{}]interface{}:
+                               val = v.searchMapWithPathPrefixes(cast.ToStringMap(next), path[i:])
+                       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
+                               val = v.searchMapWithPathPrefixes(next.(map[string]interface{}), path[i:])
+                       default:
+                               // got a value but nested key expected, do nothing and look for next prefix
+                       }
+                       if val != nil {
+                               return val
+                       }
+               }
+       }
+
+       // not found
+       return nil
+}
+
+// isPathShadowedInDeepMap makes sure the given path is not shadowed somewhere
+// on its path in the map.
+// e.g., if "foo.bar" has a value in the given map, it “shadows”
+//       "foo.bar.baz" in a lower-priority map
+func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]interface{}) string {
+       var parentVal interface{}
+       for i := 1; i < len(path); i++ {
+               parentVal = v.searchMap(m, path[0:i])
+               if parentVal == nil {
+                       // not found, no need to add more path elements
+                       return ""
+               }
+               switch parentVal.(type) {
+               case map[interface{}]interface{}:
+                       continue
+               case map[string]interface{}:
+                       continue
+               default:
+                       // parentVal is a regular value which shadows "path"
+                       return strings.Join(path[0:i], v.keyDelim)
+               }
+       }
+       return ""
+}
+
+// isPathShadowedInFlatMap makes sure the given path is not shadowed somewhere
+// in a sub-path of the map.
+// e.g., if "foo.bar" has a value in the given map, it “shadows”
+//       "foo.bar.baz" in a lower-priority map
+func (v *Viper) isPathShadowedInFlatMap(path []string, mi interface{}) string {
+       // unify input map
+       var m map[string]interface{}
+       switch mi.(type) {
+       case map[string]string, map[string]FlagValue:
+               m = cast.ToStringMap(mi)
+       default:
+               return ""
+       }
+
+       // scan paths
+       var parentKey string
+       for i := 1; i < len(path); i++ {
+               parentKey = strings.Join(path[0:i], v.keyDelim)
+               if _, ok := m[parentKey]; ok {
+                       return parentKey
+               }
+       }
+       return ""
+}
+
+// isPathShadowedInAutoEnv makes sure the given path is not shadowed somewhere
+// in the environment, when automatic env is on.
+// e.g., if "foo.bar" has a value in the environment, it “shadows”
+//       "foo.bar.baz" in a lower-priority map
+func (v *Viper) isPathShadowedInAutoEnv(path []string) string {
+       var parentKey string
+       var val string
+       for i := 1; i < len(path); i++ {
+               parentKey = strings.Join(path[0:i], v.keyDelim)
+               if val = v.getEnv(v.mergeWithEnvPrefix(parentKey)); val != "" {
+                       return parentKey
+               }
+       }
+       return ""
+}
+
+// SetTypeByDefaultValue enables or disables the inference of a key value's
+// type when the Get function is used based upon a key's default value as
+// opposed to the value returned based on the normal fetch logic.
+//
+// For example, if a key has a default value of []string{} and the same key
+// is set via an environment variable to "a b c", a call to the Get function
+// would return a string slice for the key if the key's type is inferred by
+// the default value and the Get function would return:
+//
+//   []string {"a", "b", "c"}
+//
+// Otherwise the Get function would return:
+//
+//   "a b c"
+func SetTypeByDefaultValue(enable bool) { v.SetTypeByDefaultValue(enable) }
+func (v *Viper) SetTypeByDefaultValue(enable bool) {
+       v.typeByDefValue = enable
+}
+
+// GetViper gets the global Viper instance.
+func GetViper() *Viper {
+       return v
+}
+
+// Get can retrieve any value given the key to use.
+// Get is case-insensitive for a key.
+// Get has the behavior of returning the value associated with the first
+// place from where it is set. Viper will check in the following order:
+// override, flag, env, config file, key/value store, default
+//
+// 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{} {
+       lcaseKey := strings.ToLower(key)
+       val := v.find(lcaseKey)
+       if val == nil {
+               return nil
+       }
+
+       if v.typeByDefValue {
+               // TODO(bep) this branch isn't covered by a single test.
+               valType := val
+               path := strings.Split(lcaseKey, v.keyDelim)
+               defVal := v.searchMap(v.defaults, path)
+               if defVal != nil {
+                       valType = defVal
+               }
+
+               switch valType.(type) {
+               case bool:
+                       return cast.ToBool(val)
+               case string:
+                       return cast.ToString(val)
+               case int64, int32, int16, int8, int:
+                       return cast.ToInt(val)
+               case float64, float32:
+                       return cast.ToFloat64(val)
+               case time.Time:
+                       return cast.ToTime(val)
+               case time.Duration:
+                       return cast.ToDuration(val)
+               case []string:
+                       return cast.ToStringSlice(val)
+               }
+       }
+
+       return val
+}
+
+// Sub returns new Viper instance representing a sub tree of this instance.
+// Sub is case-insensitive for a key.
+func Sub(key string) *Viper { return v.Sub(key) }
+func (v *Viper) Sub(key string) *Viper {
+       subv := New()
+       data := v.Get(key)
+       if data == nil {
+               return nil
+       }
+
+       if reflect.TypeOf(data).Kind() == reflect.Map {
+               subv.config = cast.ToStringMap(data)
+               return subv
+       }
+       return nil
+}
+
+// GetString returns the value associated with the key as a string.
+func GetString(key string) string { return v.GetString(key) }
+func (v *Viper) GetString(key string) string {
+       return cast.ToString(v.Get(key))
+}
+
+// GetBool returns the value associated with the key as a boolean.
+func GetBool(key string) bool { return v.GetBool(key) }
+func (v *Viper) GetBool(key string) bool {
+       return cast.ToBool(v.Get(key))
+}
+
+// GetInt returns the value associated with the key as an integer.
+func GetInt(key string) int { return v.GetInt(key) }
+func (v *Viper) GetInt(key string) int {
+       return cast.ToInt(v.Get(key))
+}
+
+// GetInt64 returns the value associated with the key as an integer.
+func GetInt64(key string) int64 { return v.GetInt64(key) }
+func (v *Viper) GetInt64(key string) int64 {
+       return cast.ToInt64(v.Get(key))
+}
+
+// GetFloat64 returns the value associated with the key as a float64.
+func GetFloat64(key string) float64 { return v.GetFloat64(key) }
+func (v *Viper) GetFloat64(key string) float64 {
+       return cast.ToFloat64(v.Get(key))
+}
+
+// GetTime returns the value associated with the key as time.
+func GetTime(key string) time.Time { return v.GetTime(key) }
+func (v *Viper) GetTime(key string) time.Time {
+       return cast.ToTime(v.Get(key))
+}
+
+// GetDuration returns the value associated with the key as a duration.
+func GetDuration(key string) time.Duration { return v.GetDuration(key) }
+func (v *Viper) GetDuration(key string) time.Duration {
+       return cast.ToDuration(v.Get(key))
+}
+
+// GetStringSlice returns the value associated with the key as a slice of strings.
+func GetStringSlice(key string) []string { return v.GetStringSlice(key) }
+func (v *Viper) GetStringSlice(key string) []string {
+       return cast.ToStringSlice(v.Get(key))
+}
+
+// GetStringMap returns the value associated with the key as a map of interfaces.
+func GetStringMap(key string) map[string]interface{} { return v.GetStringMap(key) }
+func (v *Viper) GetStringMap(key string) map[string]interface{} {
+       return cast.ToStringMap(v.Get(key))
+}
+
+// GetStringMapString returns the value associated with the key as a map of strings.
+func GetStringMapString(key string) map[string]string { return v.GetStringMapString(key) }
+func (v *Viper) GetStringMapString(key string) map[string]string {
+       return cast.ToStringMapString(v.Get(key))
+}
+
+// GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings.
+func GetStringMapStringSlice(key string) map[string][]string { return v.GetStringMapStringSlice(key) }
+func (v *Viper) GetStringMapStringSlice(key string) map[string][]string {
+       return cast.ToStringMapStringSlice(v.Get(key))
+}
+
+// GetSizeInBytes returns the size of the value associated with the given key
+// in bytes.
+func GetSizeInBytes(key string) uint { return v.GetSizeInBytes(key) }
+func (v *Viper) GetSizeInBytes(key string) uint {
+       sizeStr := cast.ToString(v.Get(key))
+       return parseSizeInBytes(sizeStr)
+}
+
+// UnmarshalKey takes a single key and unmarshals it into a Struct.
+func UnmarshalKey(key string, rawVal interface{}) error { return v.UnmarshalKey(key, rawVal) }
+func (v *Viper) UnmarshalKey(key string, rawVal interface{}) error {
+       err := decode(v.Get(key), defaultDecoderConfig(rawVal))
+
+       if err != nil {
+               return err
+       }
+
+       v.insensitiviseMaps()
+
+       return nil
+}
+
+// Unmarshal unmarshals the config into a Struct. Make sure that the tags
+// on the fields of the structure are properly set.
+func Unmarshal(rawVal interface{}) error { return v.Unmarshal(rawVal) }
+func (v *Viper) Unmarshal(rawVal interface{}) error {
+       err := decode(v.AllSettings(), defaultDecoderConfig(rawVal))
+
+       if err != nil {
+               return err
+       }
+
+       v.insensitiviseMaps()
+
+       return nil
+}
+
+// defaultDecoderConfig returns default mapsstructure.DecoderConfig with suppot
+// of time.Duration values & string slices
+func defaultDecoderConfig(output interface{}) *mapstructure.DecoderConfig {
+       return &mapstructure.DecoderConfig{
+               Metadata:         nil,
+               Result:           output,
+               WeaklyTypedInput: true,
+               DecodeHook: mapstructure.ComposeDecodeHookFunc(
+                       mapstructure.StringToTimeDurationHookFunc(),
+                       mapstructure.StringToSliceHookFunc(","),
+               ),
+       }
+}
+
+// A wrapper around mapstructure.Decode that mimics the WeakDecode functionality
+func decode(input interface{}, config *mapstructure.DecoderConfig) error {
+       decoder, err := mapstructure.NewDecoder(config)
+       if err != nil {
+               return err
+       }
+       return decoder.Decode(input)
+}
+
+// UnmarshalExact unmarshals the config into a Struct, erroring if a field is nonexistent
+// in the destination struct.
+func (v *Viper) UnmarshalExact(rawVal interface{}) error {
+       config := defaultDecoderConfig(rawVal)
+       config.ErrorUnused = true
+
+       err := decode(v.AllSettings(), config)
+
+       if err != nil {
+               return err
+       }
+
+       v.insensitiviseMaps()
+
+       return nil
+}
+
+// BindPFlags binds a full flag set to the configuration, using each flag's long
+// name as the config key.
+func BindPFlags(flags *pflag.FlagSet) error { return v.BindPFlags(flags) }
+func (v *Viper) BindPFlags(flags *pflag.FlagSet) error {
+       return v.BindFlagValues(pflagValueSet{flags})
+}
+
+// BindPFlag binds a specific key to a pflag (as used by cobra).
+// Example (where serverCmd is a Cobra instance):
+//
+//      serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
+//      Viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
+//
+func BindPFlag(key string, flag *pflag.Flag) error { return v.BindPFlag(key, flag) }
+func (v *Viper) BindPFlag(key string, flag *pflag.Flag) error {
+       return v.BindFlagValue(key, pflagValue{flag})
+}
+
+// BindFlagValues binds a full FlagValue set to the configuration, using each flag's long
+// name as the config key.
+func BindFlagValues(flags FlagValueSet) error { return v.BindFlagValues(flags) }
+func (v *Viper) BindFlagValues(flags FlagValueSet) (err error) {
+       flags.VisitAll(func(flag FlagValue) {
+               if err = v.BindFlagValue(flag.Name(), flag); err != nil {
+                       return
+               }
+       })
+       return nil
+}
+
+// BindFlagValue binds a specific key to a FlagValue.
+// Example (where serverCmd is a Cobra instance):
+//
+//      serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
+//      Viper.BindFlagValue("port", serverCmd.Flags().Lookup("port"))
+//
+func BindFlagValue(key string, flag FlagValue) error { return v.BindFlagValue(key, flag) }
+func (v *Viper) BindFlagValue(key string, flag FlagValue) error {
+       if flag == nil {
+               return fmt.Errorf("flag for %q is nil", key)
+       }
+       v.pflags[strings.ToLower(key)] = flag
+       return nil
+}
+
+// BindEnv binds a Viper key to a ENV variable.
+// ENV variables are case sensitive.
+// If only a key is provided, it will use the env key matching the key, uppercased.
+// EnvPrefix will be used when set when env name is not provided.
+func BindEnv(input ...string) error { return v.BindEnv(input...) }
+func (v *Viper) BindEnv(input ...string) error {
+       var key, envkey string
+       if len(input) == 0 {
+               return fmt.Errorf("BindEnv missing key to bind to")
+       }
+
+       key = strings.ToLower(input[0])
+
+       if len(input) == 1 {
+               envkey = v.mergeWithEnvPrefix(key)
+       } else {
+               envkey = input[1]
+       }
+
+       v.env[key] = envkey
+
+       return nil
+}
+
+// Given a key, find the value.
+// Viper will check in the following order:
+// flag, env, config file, key/value store, default.
+// Viper will check to see if an alias exists first.
+// Note: this assumes a lower-cased key given.
+func (v *Viper) find(lcaseKey string) interface{} {
+
+       var (
+               val    interface{}
+               exists bool
+               path   = strings.Split(lcaseKey, v.keyDelim)
+               nested = len(path) > 1
+       )
+
+       // compute the path through the nested maps to the nested value
+       if nested && v.isPathShadowedInDeepMap(path, castMapStringToMapInterface(v.aliases)) != "" {
+               return nil
+       }
+
+       // if the requested key is an alias, then return the proper key
+       lcaseKey = v.realKey(lcaseKey)
+       path = strings.Split(lcaseKey, v.keyDelim)
+       nested = len(path) > 1
+
+       // Set() override first
+       val = v.searchMap(v.override, path)
+       if val != nil {
+               return val
+       }
+       if nested && v.isPathShadowedInDeepMap(path, v.override) != "" {
+               return nil
+       }
+
+       // PFlag override next
+       flag, exists := v.pflags[lcaseKey]
+       if exists && flag.HasChanged() {
+               switch flag.ValueType() {
+               case "int", "int8", "int16", "int32", "int64":
+                       return cast.ToInt(flag.ValueString())
+               case "bool":
+                       return cast.ToBool(flag.ValueString())
+               case "stringSlice":
+                       s := strings.TrimPrefix(flag.ValueString(), "[")
+                       s = strings.TrimSuffix(s, "]")
+                       res, _ := readAsCSV(s)
+                       return res
+               default:
+                       return flag.ValueString()
+               }
+       }
+       if nested && v.isPathShadowedInFlatMap(path, v.pflags) != "" {
+               return nil
+       }
+
+       // Env override next
+       if v.automaticEnvApplied {
+               // even if it hasn't been registered, if automaticEnv is used,
+               // check any Get request
+               if val = v.getEnv(v.mergeWithEnvPrefix(lcaseKey)); val != "" {
+                       return val
+               }
+               if nested && v.isPathShadowedInAutoEnv(path) != "" {
+                       return nil
+               }
+       }
+       envkey, exists := v.env[lcaseKey]
+       if exists {
+               if val = v.getEnv(envkey); val != "" {
+                       return val
+               }
+       }
+       if nested && v.isPathShadowedInFlatMap(path, v.env) != "" {
+               return nil
+       }
+
+       // Config file next
+       val = v.searchMapWithPathPrefixes(v.config, path)
+       if val != nil {
+               return val
+       }
+       if nested && v.isPathShadowedInDeepMap(path, v.config) != "" {
+               return nil
+       }
+
+       // K/V store next
+       val = v.searchMap(v.kvstore, path)
+       if val != nil {
+               return val
+       }
+       if nested && v.isPathShadowedInDeepMap(path, v.kvstore) != "" {
+               return nil
+       }
+
+       // Default next
+       val = v.searchMap(v.defaults, path)
+       if val != nil {
+               return val
+       }
+       if nested && v.isPathShadowedInDeepMap(path, v.defaults) != "" {
+               return nil
+       }
+
+       // last chance: if no other value is returned and a flag does exist for the value,
+       // get the flag's value even if the flag's value has not changed
+       if flag, exists := v.pflags[lcaseKey]; exists {
+               switch flag.ValueType() {
+               case "int", "int8", "int16", "int32", "int64":
+                       return cast.ToInt(flag.ValueString())
+               case "bool":
+                       return cast.ToBool(flag.ValueString())
+               case "stringSlice":
+                       s := strings.TrimPrefix(flag.ValueString(), "[")
+                       s = strings.TrimSuffix(s, "]")
+                       res, _ := readAsCSV(s)
+                       return res
+               default:
+                       return flag.ValueString()
+               }
+       }
+       // last item, no need to check shadowing
+
+       return nil
+}
+
+func readAsCSV(val string) ([]string, error) {
+       if val == "" {
+               return []string{}, nil
+       }
+       stringReader := strings.NewReader(val)
+       csvReader := csv.NewReader(stringReader)
+       return csvReader.Read()
+}
+
+// IsSet checks to see if the key has been set in any of the data locations.
+// IsSet is case-insensitive for a key.
+func IsSet(key string) bool { return v.IsSet(key) }
+func (v *Viper) IsSet(key string) bool {
+       lcaseKey := strings.ToLower(key)
+       val := v.find(lcaseKey)
+       return val != nil
+}
+
+// AutomaticEnv has Viper check ENV variables for all.
+// keys set in config, default & flags
+func AutomaticEnv() { v.AutomaticEnv() }
+func (v *Viper) AutomaticEnv() {
+       v.automaticEnvApplied = true
+}
+
+// SetEnvKeyReplacer sets the strings.Replacer on the viper object
+// Useful for mapping an environmental variable to a key that does
+// not match it.
+func SetEnvKeyReplacer(r *strings.Replacer) { v.SetEnvKeyReplacer(r) }
+func (v *Viper) SetEnvKeyReplacer(r *strings.Replacer) {
+       v.envKeyReplacer = r
+}
+
+// Aliases provide another accessor for the same key.
+// This enables one to change a name without breaking the application
+func RegisterAlias(alias string, key string) { v.RegisterAlias(alias, key) }
+func (v *Viper) RegisterAlias(alias string, key string) {
+       v.registerAlias(alias, strings.ToLower(key))
+}
+
+func (v *Viper) registerAlias(alias string, key string) {
+       alias = strings.ToLower(alias)
+       if alias != key && alias != v.realKey(key) {
+               _, exists := v.aliases[alias]
+
+               if !exists {
+                       // if we alias something that exists in one of the maps to another
+                       // name, we'll never be able to get that value using the original
+                       // name, so move the config value to the new realkey.
+                       if val, ok := v.config[alias]; ok {
+                               delete(v.config, alias)
+                               v.config[key] = val
+                       }
+                       if val, ok := v.kvstore[alias]; ok {
+                               delete(v.kvstore, alias)
+                               v.kvstore[key] = val
+                       }
+                       if val, ok := v.defaults[alias]; ok {
+                               delete(v.defaults, alias)
+                               v.defaults[key] = val
+                       }
+                       if val, ok := v.override[alias]; ok {
+                               delete(v.override, alias)
+                               v.override[key] = val
+                       }
+                       v.aliases[alias] = key
+               }
+       } else {
+               jww.WARN.Println("Creating circular reference alias", alias, key, v.realKey(key))
+       }
+}
+
+func (v *Viper) realKey(key string) string {
+       newkey, exists := v.aliases[key]
+       if exists {
+               jww.DEBUG.Println("Alias", key, "to", newkey)
+               return v.realKey(newkey)
+       }
+       return key
+}
+
+// InConfig checks to see if the given key (or an alias) is in the config file.
+func InConfig(key string) bool { return v.InConfig(key) }
+func (v *Viper) InConfig(key string) bool {
+       // if the requested key is an alias, then return the proper key
+       key = v.realKey(key)
+
+       _, exists := v.config[key]
+       return exists
+}
+
+// SetDefault sets the default value for this key.
+// SetDefault is case-insensitive for a key.
+// Default only used when no value is provided by the user via flag, config or ENV.
+func SetDefault(key string, value interface{}) { v.SetDefault(key, value) }
+func (v *Viper) SetDefault(key string, value interface{}) {
+       // If alias passed in, then set the proper default
+       key = v.realKey(strings.ToLower(key))
+       value = toCaseInsensitiveValue(value)
+
+       path := strings.Split(key, v.keyDelim)
+       lastKey := strings.ToLower(path[len(path)-1])
+       deepestMap := deepSearch(v.defaults, path[0:len(path)-1])
+
+       // set innermost value
+       deepestMap[lastKey] = value
+}
+
+// Set sets the value for the key in the override regiser.
+// Set is case-insensitive for a key.
+// Will be used instead of values obtained via
+// flags, config file, ENV, default, or key/value store.
+func Set(key string, value interface{}) { v.Set(key, value) }
+func (v *Viper) Set(key string, value interface{}) {
+       // If alias passed in, then set the proper override
+       key = v.realKey(strings.ToLower(key))
+       value = toCaseInsensitiveValue(value)
+
+       path := strings.Split(key, v.keyDelim)
+       lastKey := strings.ToLower(path[len(path)-1])
+       deepestMap := deepSearch(v.override, path[0:len(path)-1])
+
+       // set innermost value
+       deepestMap[lastKey] = value
+}
+
+// ReadInConfig will discover and load the configuration file from disk
+// and key/value stores, searching in one of the defined paths.
+func ReadInConfig() error { return v.ReadInConfig() }
+func (v *Viper) ReadInConfig() error {
+       jww.INFO.Println("Attempting to read in config file")
+       filename, err := v.getConfigFile()
+       if err != nil {
+               return err
+       }
+
+       if !stringInSlice(v.getConfigType(), SupportedExts) {
+               return UnsupportedConfigError(v.getConfigType())
+       }
+
+       file, err := afero.ReadFile(v.fs, filename)
+       if err != nil {
+               return err
+       }
+
+       config := make(map[string]interface{})
+
+       err = v.unmarshalReader(bytes.NewReader(file), config)
+       if err != nil {
+               return err
+       }
+
+       v.config = config
+       return nil
+}
+
+// MergeInConfig merges a new configuration with an existing config.
+func MergeInConfig() error { return v.MergeInConfig() }
+func (v *Viper) MergeInConfig() error {
+       jww.INFO.Println("Attempting to merge in config file")
+       filename, err := v.getConfigFile()
+       if err != nil {
+               return err
+       }
+
+       if !stringInSlice(v.getConfigType(), SupportedExts) {
+               return UnsupportedConfigError(v.getConfigType())
+       }
+
+       file, err := afero.ReadFile(v.fs, filename)
+       if err != nil {
+               return err
+       }
+
+       return v.MergeConfig(bytes.NewReader(file))
+}
+
+// ReadConfig will read a configuration file, setting existing keys to nil if the
+// key does not exist in the file.
+func ReadConfig(in io.Reader) error { return v.ReadConfig(in) }
+func (v *Viper) ReadConfig(in io.Reader) error {
+       v.config = make(map[string]interface{})
+       return v.unmarshalReader(in, v.config)
+}
+
+// MergeConfig merges a new configuration with an existing config.
+func MergeConfig(in io.Reader) error { return v.MergeConfig(in) }
+func (v *Viper) MergeConfig(in io.Reader) error {
+       if v.config == nil {
+               v.config = make(map[string]interface{})
+       }
+       cfg := make(map[string]interface{})
+       if err := v.unmarshalReader(in, cfg); err != nil {
+               return err
+       }
+       mergeMaps(cfg, v.config, nil)
+       return nil
+}
+
+func keyExists(k string, m map[string]interface{}) string {
+       lk := strings.ToLower(k)
+       for mk := range m {
+               lmk := strings.ToLower(mk)
+               if lmk == lk {
+                       return mk
+               }
+       }
+       return ""
+}
+
+func castToMapStringInterface(
+       src map[interface{}]interface{}) map[string]interface{} {
+       tgt := map[string]interface{}{}
+       for k, v := range src {
+               tgt[fmt.Sprintf("%v", k)] = v
+       }
+       return tgt
+}
+
+func castMapStringToMapInterface(src map[string]string) map[string]interface{} {
+       tgt := map[string]interface{}{}
+       for k, v := range src {
+               tgt[k] = v
+       }
+       return tgt
+}
+
+func castMapFlagToMapInterface(src map[string]FlagValue) map[string]interface{} {
+       tgt := map[string]interface{}{}
+       for k, v := range src {
+               tgt[k] = v
+       }
+       return tgt
+}
+
+// mergeMaps merges two maps. The `itgt` parameter is for handling go-yaml's
+// insistence on parsing nested structures as `map[interface{}]interface{}`
+// instead of using a `string` as the key for nest structures beyond one level
+// deep. Both map types are supported as there is a go-yaml fork that uses
+// `map[string]interface{}` instead.
+func mergeMaps(
+       src, tgt map[string]interface{}, itgt map[interface{}]interface{}) {
+       for sk, sv := range src {
+               tk := keyExists(sk, tgt)
+               if tk == "" {
+                       jww.TRACE.Printf("tk=\"\", tgt[%s]=%v", sk, sv)
+                       tgt[sk] = sv
+                       if itgt != nil {
+                               itgt[sk] = sv
+                       }
+                       continue
+               }
+
+               tv, ok := tgt[tk]
+               if !ok {
+                       jww.TRACE.Printf("tgt[%s] != ok, tgt[%s]=%v", tk, sk, sv)
+                       tgt[sk] = sv
+                       if itgt != nil {
+                               itgt[sk] = sv
+                       }
+                       continue
+               }
+
+               svType := reflect.TypeOf(sv)
+               tvType := reflect.TypeOf(tv)
+               if svType != tvType {
+                       jww.ERROR.Printf(
+                               "svType != tvType; key=%s, st=%v, tt=%v, sv=%v, tv=%v",
+                               sk, svType, tvType, sv, tv)
+                       continue
+               }
+
+               jww.TRACE.Printf("processing key=%s, st=%v, tt=%v, sv=%v, tv=%v",
+                       sk, svType, tvType, sv, tv)
+
+               switch ttv := tv.(type) {
+               case map[interface{}]interface{}:
+                       jww.TRACE.Printf("merging maps (must convert)")
+                       tsv := sv.(map[interface{}]interface{})
+                       ssv := castToMapStringInterface(tsv)
+                       stv := castToMapStringInterface(ttv)
+                       mergeMaps(ssv, stv, ttv)
+               case map[string]interface{}:
+                       jww.TRACE.Printf("merging maps")
+                       mergeMaps(sv.(map[string]interface{}), ttv, nil)
+               default:
+                       jww.TRACE.Printf("setting value")
+                       tgt[tk] = sv
+                       if itgt != nil {
+                               itgt[tk] = sv
+                       }
+               }
+       }
+}
+
+// ReadRemoteConfig attempts to get configuration from a remote source
+// and read it in the remote configuration registry.
+func ReadRemoteConfig() error { return v.ReadRemoteConfig() }
+func (v *Viper) ReadRemoteConfig() error {
+       return v.getKeyValueConfig()
+}
+
+func WatchRemoteConfig() error { return v.WatchRemoteConfig() }
+func (v *Viper) WatchRemoteConfig() error {
+       return v.watchKeyValueConfig()
+}
+
+func (v *Viper) WatchRemoteConfigOnChannel() error {
+       return v.watchKeyValueConfigOnChannel()
+}
+
+// Unmarshal a Reader into a map.
+// Should probably be an unexported function.
+func unmarshalReader(in io.Reader, c map[string]interface{}) error {
+       return v.unmarshalReader(in, c)
+}
+
+func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error {
+       return unmarshallConfigReader(in, c, v.getConfigType())
+}
+
+func (v *Viper) insensitiviseMaps() {
+       insensitiviseMap(v.config)
+       insensitiviseMap(v.defaults)
+       insensitiviseMap(v.override)
+       insensitiviseMap(v.kvstore)
+}
+
+// Retrieve the first found remote configuration.
+func (v *Viper) getKeyValueConfig() error {
+       if RemoteConfig == nil {
+               return RemoteConfigError("Enable the remote features by doing a blank import of the viper/remote package: '_ github.com/spf13/viper/remote'")
+       }
+
+       for _, rp := range v.remoteProviders {
+               val, err := v.getRemoteConfig(rp)
+               if err != nil {
+                       continue
+               }
+               v.kvstore = val
+               return nil
+       }
+       return RemoteConfigError("No Files Found")
+}
+
+func (v *Viper) getRemoteConfig(provider RemoteProvider) (map[string]interface{}, error) {
+       reader, err := RemoteConfig.Get(provider)
+       if err != nil {
+               return nil, err
+       }
+       err = v.unmarshalReader(reader, v.kvstore)
+       return v.kvstore, err
+}
+
+// Retrieve the first found remote configuration.
+func (v *Viper) watchKeyValueConfigOnChannel() error {
+       for _, rp := range v.remoteProviders {
+               respc, _ := RemoteConfig.WatchChannel(rp)
+               //Todo: Add quit channel
+               go func(rc <-chan *RemoteResponse) {
+                       for {
+                               b := <-rc
+                               reader := bytes.NewReader(b.Value)
+                               v.unmarshalReader(reader, v.kvstore)
+                       }
+               }(respc)
+               return nil
+       }
+       return RemoteConfigError("No Files Found")
+}
+
+// Retrieve the first found remote configuration.
+func (v *Viper) watchKeyValueConfig() error {
+       for _, rp := range v.remoteProviders {
+               val, err := v.watchRemoteConfig(rp)
+               if err != nil {
+                       continue
+               }
+               v.kvstore = val
+               return nil
+       }
+       return RemoteConfigError("No Files Found")
+}
+
+func (v *Viper) watchRemoteConfig(provider RemoteProvider) (map[string]interface{}, error) {
+       reader, err := RemoteConfig.Watch(provider)
+       if err != nil {
+               return nil, err
+       }
+       err = v.unmarshalReader(reader, v.kvstore)
+       return v.kvstore, err
+}
+
+// AllKeys returns all keys holding a value, regardless of where they are set.
+// Nested keys are returned with a v.keyDelim (= ".") separator
+func AllKeys() []string { return v.AllKeys() }
+func (v *Viper) AllKeys() []string {
+       m := map[string]bool{}
+       // add all paths, by order of descending priority to ensure correct shadowing
+       m = v.flattenAndMergeMap(m, castMapStringToMapInterface(v.aliases), "")
+       m = v.flattenAndMergeMap(m, v.override, "")
+       m = v.mergeFlatMap(m, castMapFlagToMapInterface(v.pflags))
+       m = v.mergeFlatMap(m, castMapStringToMapInterface(v.env))
+       m = v.flattenAndMergeMap(m, v.config, "")
+       m = v.flattenAndMergeMap(m, v.kvstore, "")
+       m = v.flattenAndMergeMap(m, v.defaults, "")
+
+       // convert set of paths to list
+       a := []string{}
+       for x := range m {
+               a = append(a, x)
+       }
+       return a
+}
+
+// flattenAndMergeMap recursively flattens the given map into a map[string]bool
+// of key paths (used as a set, easier to manipulate than a []string):
+// - each path is merged into a single key string, delimited with v.keyDelim (= ".")
+// - if a path is shadowed by an earlier value in the initial shadow map,
+//   it is skipped.
+// The resulting set of paths is merged to the given shadow set at the same time.
+func (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[string]interface{}, prefix string) map[string]bool {
+       if shadow != nil && prefix != "" && shadow[prefix] {
+               // prefix is shadowed => nothing more to flatten
+               return shadow
+       }
+       if shadow == nil {
+               shadow = make(map[string]bool)
+       }
+
+       var m2 map[string]interface{}
+       if prefix != "" {
+               prefix += v.keyDelim
+       }
+       for k, val := range m {
+               fullKey := prefix + k
+               switch val.(type) {
+               case map[string]interface{}:
+                       m2 = val.(map[string]interface{})
+               case map[interface{}]interface{}:
+                       m2 = cast.ToStringMap(val)
+               default:
+                       // immediate value
+                       shadow[strings.ToLower(fullKey)] = true
+                       continue
+               }
+               // recursively merge to shadow map
+               shadow = v.flattenAndMergeMap(shadow, m2, fullKey)
+       }
+       return shadow
+}
+
+// mergeFlatMap merges the given maps, excluding values of the second map
+// shadowed by values from the first map.
+func (v *Viper) mergeFlatMap(shadow map[string]bool, m map[string]interface{}) map[string]bool {
+       // scan keys
+outer:
+       for k, _ := range m {
+               path := strings.Split(k, v.keyDelim)
+               // scan intermediate paths
+               var parentKey string
+               for i := 1; i < len(path); i++ {
+                       parentKey = strings.Join(path[0:i], v.keyDelim)
+                       if shadow[parentKey] {
+                               // path is shadowed, continue
+                               continue outer
+                       }
+               }
+               // add key
+               shadow[strings.ToLower(k)] = true
+       }
+       return shadow
+}
+
+// AllSettings merges all settings and returns them as a map[string]interface{}.
+func AllSettings() map[string]interface{} { return v.AllSettings() }
+func (v *Viper) AllSettings() map[string]interface{} {
+       m := map[string]interface{}{}
+       // start from the list of keys, and construct the map one value at a time
+       for _, k := range v.AllKeys() {
+               value := v.Get(k)
+               if value == nil {
+                       // should not happen, since AllKeys() returns only keys holding a value,
+                       // check just in case anything changes
+                       continue
+               }
+               path := strings.Split(k, v.keyDelim)
+               lastKey := strings.ToLower(path[len(path)-1])
+               deepestMap := deepSearch(m, path[0:len(path)-1])
+               // set innermost value
+               deepestMap[lastKey] = value
+       }
+       return m
+}
+
+// SetFs sets the filesystem to use to read configuration.
+func SetFs(fs afero.Fs) { v.SetFs(fs) }
+func (v *Viper) SetFs(fs afero.Fs) {
+       v.fs = fs
+}
+
+// SetConfigName sets name for the config file.
+// Does not include extension.
+func SetConfigName(in string) { v.SetConfigName(in) }
+func (v *Viper) SetConfigName(in string) {
+       if in != "" {
+               v.configName = in
+               v.configFile = ""
+       }
+}
+
+// SetConfigType sets the type of the configuration returned by the
+// remote source, e.g. "json".
+func SetConfigType(in string) { v.SetConfigType(in) }
+func (v *Viper) SetConfigType(in string) {
+       if in != "" {
+               v.configType = in
+       }
+}
+
+func (v *Viper) getConfigType() string {
+       if v.configType != "" {
+               return v.configType
+       }
+
+       cf, err := v.getConfigFile()
+       if err != nil {
+               return ""
+       }
+
+       ext := filepath.Ext(cf)
+
+       if len(ext) > 1 {
+               return ext[1:]
+       }
+
+       return ""
+}
+
+func (v *Viper) getConfigFile() (string, error) {
+       // if explicitly set, then use it
+       if v.configFile != "" {
+               return v.configFile, nil
+       }
+
+       cf, err := v.findConfigFile()
+       if err != nil {
+               return "", err
+       }
+
+       v.configFile = cf
+       return v.getConfigFile()
+}
+
+func (v *Viper) searchInPath(in string) (filename string) {
+       jww.DEBUG.Println("Searching for config in ", in)
+       for _, ext := range SupportedExts {
+               jww.DEBUG.Println("Checking for", filepath.Join(in, v.configName+"."+ext))
+               if b, _ := exists(filepath.Join(in, v.configName+"."+ext)); b {
+                       jww.DEBUG.Println("Found: ", filepath.Join(in, v.configName+"."+ext))
+                       return filepath.Join(in, v.configName+"."+ext)
+               }
+       }
+
+       return ""
+}
+
+// Search all configPaths for any config file.
+// Returns the first path that exists (and is a config file).
+func (v *Viper) findConfigFile() (string, error) {
+       jww.INFO.Println("Searching for config in ", v.configPaths)
+
+       for _, cp := range v.configPaths {
+               file := v.searchInPath(cp)
+               if file != "" {
+                       return file, nil
+               }
+       }
+       return "", ConfigFileNotFoundError{v.configName, fmt.Sprintf("%s", v.configPaths)}
+}
+
+// Debug prints all configuration registries for debugging
+// purposes.
+func Debug() { v.Debug() }
+func (v *Viper) Debug() {
+       fmt.Printf("Aliases:\n%#v\n", v.aliases)
+       fmt.Printf("Override:\n%#v\n", v.override)
+       fmt.Printf("PFlags:\n%#v\n", v.pflags)
+       fmt.Printf("Env:\n%#v\n", v.env)
+       fmt.Printf("Key/Value Store:\n%#v\n", v.kvstore)
+       fmt.Printf("Config:\n%#v\n", v.config)
+       fmt.Printf("Defaults:\n%#v\n", v.defaults)
+}