From bd4eb4285bd8c8304528d59ab3578c96d578f779 Mon Sep 17 00:00:00 2001 From: victor Date: Tue, 8 Sep 2020 09:20:53 +0800 Subject: [PATCH] remove test add logger for viper --- flags.go | 2 +- flags_test.go | 65 ---- go.mod | 16 +- go.sum | 25 ++ overrides_test.go | 173 --------- util.go | 6 +- util_test.go | 54 --- viper.go | 343 +++++++++++++++--- viper_test.go | 870 ---------------------------------------------- 9 files changed, 333 insertions(+), 1221 deletions(-) delete mode 100644 flags_test.go delete mode 100644 overrides_test.go delete mode 100644 util_test.go delete mode 100644 viper_test.go diff --git a/flags.go b/flags.go index dd32f4e..b5ddbf5 100644 --- a/flags.go +++ b/flags.go @@ -36,7 +36,7 @@ type pflagValue struct { flag *pflag.Flag } -// HasChanges returns whether the flag has changes or not. +// HasChanged returns whether the flag has changes or not. func (p pflagValue) HasChanged() bool { return p.flag.Changed } diff --git a/flags_test.go b/flags_test.go deleted file mode 100644 index 0b976b6..0000000 --- a/flags_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package viper - -import ( - "testing" - - "github.com/spf13/pflag" - "github.com/stretchr/testify/assert" -) - -func TestBindFlagValueSet(t *testing.T) { - flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError) - - var testValues = map[string]*string{ - "host": nil, - "port": nil, - "endpoint": nil, - } - - var mutatedTestValues = map[string]string{ - "host": "localhost", - "port": "6060", - "endpoint": "/public", - } - - for name := range testValues { - testValues[name] = flagSet.String(name, "", "test") - } - - flagValueSet := pflagValueSet{flagSet} - - err := BindFlagValues(flagValueSet) - if err != nil { - t.Fatalf("error binding flag set, %v", err) - } - - flagSet.VisitAll(func(flag *pflag.Flag) { - flag.Value.Set(mutatedTestValues[flag.Name]) - flag.Changed = true - }) - - for name, expected := range mutatedTestValues { - assert.Equal(t, Get(name), expected) - } -} - -func TestBindFlagValue(t *testing.T) { - var testString = "testing" - var testValue = newStringValue(testString, &testString) - - flag := &pflag.Flag{ - Name: "testflag", - Value: testValue, - Changed: false, - } - - flagValue := pflagValue{flag} - BindFlagValue("testvalue", flagValue) - - assert.Equal(t, testString, Get("testvalue")) - - flag.Value.Set("testing_mutate") - flag.Changed = true //hack for pflag usage - - assert.Equal(t, "testing_mutate", Get("testvalue")) -} diff --git a/go.mod b/go.mod index d3387a5..ad45af6 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,12 @@ module github.com/xurwxj/viper +go 1.15 + require ( - github.com/fsnotify/fsnotify v1.4.7 - github.com/mitchellh/mapstructure v1.0.0 - github.com/spf13/afero v1.1.2 - github.com/spf13/cast v1.2.0 - github.com/spf13/jwalterweatherman v1.0.0 - github.com/spf13/pflag v1.0.2 - golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992 // indirect - golang.org/x/text v0.3.0 // indirect + github.com/fsnotify/fsnotify v1.4.9 + github.com/mitchellh/mapstructure v1.3.3 + github.com/spf13/afero v1.3.5 + github.com/spf13/cast v1.3.1 + github.com/spf13/pflag v1.0.5 + golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f // indirect ) diff --git a/go.sum b/go.sum index 6763604..cbb4eb2 100644 --- a/go.sum +++ b/go.sum @@ -1,18 +1,43 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/mitchellh/mapstructure v1.0.0 h1:vVpGvMXJPqSDh2VYHF7gsfQj8Ncx+Xw5Y1KHeTRY+7I= github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.3.5/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.2.0 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg= github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.2 h1:Fy0orTDgHdbnzHcsOgfCN4LtHf0ec3wwtiwJqwvf3Gc= github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992 h1:BH3eQWeGbwRU2+wxxuuPOdFBmaiBH81O8BugSjHeTFg= golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f h1:Fqb3ao1hUmOR3GkUOg/Y+BadLwykBIzs5q8Ez2SbHyc= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/overrides_test.go b/overrides_test.go deleted file mode 100644 index dd2aa9b..0000000 --- a/overrides_test.go +++ /dev/null @@ -1,173 +0,0 @@ -package viper - -import ( - "fmt" - "strings" - "testing" - - "github.com/spf13/cast" - "github.com/stretchr/testify/assert" -) - -type layer int - -const ( - defaultLayer layer = iota + 1 - overrideLayer -) - -func TestNestedOverrides(t *testing.T) { - assert := assert.New(t) - var v *Viper - - // Case 0: value overridden by a value - overrideDefault(assert, "tom", 10, "tom", 20) // "tom" is first given 10 as default value, then overridden by 20 - override(assert, "tom", 10, "tom", 20) // "tom" is first given value 10, then overridden by 20 - overrideDefault(assert, "tom.age", 10, "tom.age", 20) - override(assert, "tom.age", 10, "tom.age", 20) - overrideDefault(assert, "sawyer.tom.age", 10, "sawyer.tom.age", 20) - override(assert, "sawyer.tom.age", 10, "sawyer.tom.age", 20) - - // Case 1: key:value overridden by a value - v = overrideDefault(assert, "tom.age", 10, "tom", "boy") // "tom.age" is first given 10 as default value, then "tom" is overridden by "boy" - assert.Nil(v.Get("tom.age")) // "tom.age" should not exist anymore - v = override(assert, "tom.age", 10, "tom", "boy") - assert.Nil(v.Get("tom.age")) - - // Case 2: value overridden by a key:value - overrideDefault(assert, "tom", "boy", "tom.age", 10) // "tom" is first given "boy" as default value, then "tom" is overridden by map{"age":10} - override(assert, "tom.age", 10, "tom", "boy") - - // Case 3: key:value overridden by a key:value - v = overrideDefault(assert, "tom.size", 4, "tom.age", 10) - assert.Equal(4, v.Get("tom.size")) // value should still be reachable - v = override(assert, "tom.size", 4, "tom.age", 10) - assert.Equal(4, v.Get("tom.size")) - deepCheckValue(assert, v, overrideLayer, []string{"tom", "size"}, 4) - - // Case 4: key:value overridden by a map - v = overrideDefault(assert, "tom.size", 4, "tom", map[string]interface{}{"age": 10}) // "tom.size" is first given "4" as default value, then "tom" is overridden by map{"age":10} - assert.Equal(4, v.Get("tom.size")) // "tom.size" should still be reachable - assert.Equal(10, v.Get("tom.age")) // new value should be there - deepCheckValue(assert, v, overrideLayer, []string{"tom", "age"}, 10) // new value should be there - v = override(assert, "tom.size", 4, "tom", map[string]interface{}{"age": 10}) - assert.Nil(v.Get("tom.size")) - assert.Equal(10, v.Get("tom.age")) - deepCheckValue(assert, v, overrideLayer, []string{"tom", "age"}, 10) - - // Case 5: array overridden by a value - overrideDefault(assert, "tom", []int{10, 20}, "tom", 30) - override(assert, "tom", []int{10, 20}, "tom", 30) - overrideDefault(assert, "tom.age", []int{10, 20}, "tom.age", 30) - override(assert, "tom.age", []int{10, 20}, "tom.age", 30) - - // Case 6: array overridden by an array - overrideDefault(assert, "tom", []int{10, 20}, "tom", []int{30, 40}) - override(assert, "tom", []int{10, 20}, "tom", []int{30, 40}) - overrideDefault(assert, "tom.age", []int{10, 20}, "tom.age", []int{30, 40}) - v = override(assert, "tom.age", []int{10, 20}, "tom.age", []int{30, 40}) - // explicit array merge: - s, ok := v.Get("tom.age").([]int) - if assert.True(ok, "tom[\"age\"] is not a slice") { - v.Set("tom.age", append(s, []int{50, 60}...)) - assert.Equal([]int{30, 40, 50, 60}, v.Get("tom.age")) - deepCheckValue(assert, v, overrideLayer, []string{"tom", "age"}, []int{30, 40, 50, 60}) - } -} - -func overrideDefault(assert *assert.Assertions, firstPath string, firstValue interface{}, secondPath string, secondValue interface{}) *Viper { - return overrideFromLayer(defaultLayer, assert, firstPath, firstValue, secondPath, secondValue) -} -func override(assert *assert.Assertions, firstPath string, firstValue interface{}, secondPath string, secondValue interface{}) *Viper { - return overrideFromLayer(overrideLayer, assert, firstPath, firstValue, secondPath, secondValue) -} - -// overrideFromLayer performs the sequential override and low-level checks. -// -// First assignment is made on layer l for path firstPath with value firstValue, -// the second one on the override layer (i.e., with the Set() function) -// for path secondPath with value secondValue. -// -// firstPath and secondPath can include an arbitrary number of dots to indicate -// a nested element. -// -// After each assignment, the value is checked, retrieved both by its full path -// and by its key sequence (successive maps). -func overrideFromLayer(l layer, assert *assert.Assertions, firstPath string, firstValue interface{}, secondPath string, secondValue interface{}) *Viper { - v := New() - firstKeys := strings.Split(firstPath, v.keyDelim) - if assert == nil || - len(firstKeys) == 0 || len(firstKeys[0]) == 0 { - return v - } - - // Set and check first value - switch l { - case defaultLayer: - v.SetDefault(firstPath, firstValue) - case overrideLayer: - v.Set(firstPath, firstValue) - default: - return v - } - assert.Equal(firstValue, v.Get(firstPath)) - deepCheckValue(assert, v, l, firstKeys, firstValue) - - // Override and check new value - secondKeys := strings.Split(secondPath, v.keyDelim) - if len(secondKeys) == 0 || len(secondKeys[0]) == 0 { - return v - } - v.Set(secondPath, secondValue) - assert.Equal(secondValue, v.Get(secondPath)) - deepCheckValue(assert, v, overrideLayer, secondKeys, secondValue) - - return v -} - -// deepCheckValue checks that all given keys correspond to a valid path in the -// configuration map of the given layer, and that the final value equals the one given -func deepCheckValue(assert *assert.Assertions, v *Viper, l layer, keys []string, value interface{}) { - if assert == nil || v == nil || - len(keys) == 0 || len(keys[0]) == 0 { - return - } - - // init - var val interface{} - var ms string - switch l { - case defaultLayer: - val = v.defaults - ms = "v.defaults" - case overrideLayer: - val = v.override - ms = "v.override" - } - - // loop through map - var m map[string]interface{} - err := false - for _, k := range keys { - if val == nil { - assert.Fail(fmt.Sprintf("%s is not a map[string]interface{}", ms)) - return - } - - // deep scan of the map to get the final value - switch val.(type) { - case map[interface{}]interface{}: - m = cast.ToStringMap(val) - case map[string]interface{}: - m = val.(map[string]interface{}) - default: - assert.Fail(fmt.Sprintf("%s is not a map[string]interface{}", ms)) - return - } - ms = ms + "[\"" + k + "\"]" - val = m[k] - } - if !err { - assert.Equal(value, val) - } -} diff --git a/util.go b/util.go index 952cad4..3ad05be 100644 --- a/util.go +++ b/util.go @@ -20,7 +20,6 @@ import ( "github.com/spf13/afero" "github.com/spf13/cast" - jww "github.com/spf13/jwalterweatherman" ) // ConfigParseError denotes failing to parse configuration file. @@ -89,7 +88,7 @@ func insensitiviseMap(m map[string]interface{}) { } func absPathify(inPath string) string { - jww.INFO.Println("Trying to resolve absolute path to", inPath) + fmt.Println("Trying to resolve absolute path to", inPath) if strings.HasPrefix(inPath, "$HOME") { inPath = userHomeDir() + inPath[5:] @@ -109,8 +108,7 @@ func absPathify(inPath string) string { return filepath.Clean(p) } - jww.ERROR.Println("Couldn't discover absolute path") - jww.ERROR.Println(err) + fmt.Println("Couldn't discover absolute path", err) return "" } diff --git a/util_test.go b/util_test.go deleted file mode 100644 index 0af80bb..0000000 --- a/util_test.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright © 2016 Steve Francia . -// -// 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. - -package viper - -import ( - "reflect" - "testing" -) - -func TestCopyAndInsensitiviseMap(t *testing.T) { - var ( - given = map[string]interface{}{ - "Foo": 32, - "Bar": map[interface{}]interface { - }{ - "ABc": "A", - "cDE": "B"}, - } - expected = map[string]interface{}{ - "foo": 32, - "bar": map[string]interface { - }{ - "abc": "A", - "cde": "B"}, - } - ) - - got := copyAndInsensitiviseMap(given) - - if !reflect.DeepEqual(got, expected) { - t.Fatalf("Got %q\nexpected\n%q", got, expected) - } - - if _, ok := given["foo"]; ok { - t.Fatal("Input map changed") - } - - if _, ok := given["bar"]; ok { - t.Fatal("Input map changed") - } - - m := given["Bar"].(map[interface{}]interface{}) - if _, ok := m["ABc"]; !ok { - t.Fatal("Input map changed") - } -} diff --git a/viper.go b/viper.go index 49119be..a5f6676 100644 --- a/viper.go +++ b/viper.go @@ -37,7 +37,6 @@ import ( "github.com/mitchellh/mapstructure" "github.com/spf13/afero" "github.com/spf13/cast" - jww "github.com/spf13/jwalterweatherman" "github.com/spf13/pflag" ) @@ -141,8 +140,8 @@ type Viper struct { // Name of file to look for inside the path configName string configFile string - configType string envPrefix string + logger Logger automaticEnvApplied bool envKeyReplacer *strings.Replacer @@ -172,12 +171,13 @@ func New() *Viper { v.pflags = make(map[string]FlagValue) v.env = make(map[string]string) v.aliases = make(map[string]string) + v.logger = DefaultLogger(INFO) v.typeByDefValue = false return v } -// Intended for testing, will reset all to default settings. +// Reset 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() { @@ -188,26 +188,31 @@ func Reset() { // SupportedExts are universally supported extensions. var SupportedExts = []string{"json"} +// OnConfigChange are check change event func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) } + +// OnConfigChange are check change event func (v *Viper) OnConfigChange(run func(in fsnotify.Event)) { v.onConfigChange = run } +// WatchConfig watch config change func WatchConfig() { v.WatchConfig() } +// WatchConfig watch config change func (v *Viper) WatchConfig() { initWG := sync.WaitGroup{} initWG.Add(1) go func() { watcher, err := fsnotify.NewWatcher() if err != nil { - log.Fatal(err) + v.logger.Errorf("fsnotify watcher err: %v", 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.Printf("error: %v\n", err) + v.logger.Errorf("getConfigFile error: %v\n", err) return } @@ -236,7 +241,7 @@ func (v *Viper) WatchConfig() { realConfigFile = currentConfigFile err := v.ReadInConfig() if err != nil { - log.Printf("error reading config file: %v\n", err) + v.logger.Errorf("error reading config file: %v\n", err) } if v.onConfigChange != nil { v.onConfigChange(event) @@ -249,7 +254,7 @@ func (v *Viper) WatchConfig() { case err, ok := <-watcher.Errors: if ok { // 'Errors' channel is not closed - log.Printf("watcher error: %v\n", err) + v.logger.Errorf("watcher error: %v\n", err) } eventsWG.Done() return @@ -266,6 +271,9 @@ func (v *Viper) WatchConfig() { // 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) } + +// 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 (v *Viper) SetConfigFile(in string) { if in != "" { v.configFile = in @@ -276,6 +284,10 @@ func (v *Viper) SetConfigFile(in string) { // 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) } + +// 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 (v *Viper) SetEnvPrefix(in string) { if in != "" { v.envPrefix = in @@ -304,17 +316,41 @@ func (v *Viper) getEnv(key string) string { return os.Getenv(key) } +// WithLogger returns a new Options value with Logger set to the given value. +// +// Logger provides a way to configure what logger each value of badger.DB uses. +// +// The default value of Logger writes to stderr using the log package from the Go standard library. +func (v *Viper) WithLogger(val Logger) { + v.logger = val +} + +// WithLoggingLevel returns a new Options value with logging level of the +// default logger set to the given value. +// LoggingLevel sets the level of logging. It should be one of DEBUG, INFO, +// WARNING or ERROR levels. +// +// The default value of LoggingLevel is INFO. +func (v *Viper) WithLoggingLevel(val loggingLevel) { + v.logger = DefaultLogger(val) +} + +// ConfigFileUsed returns the file used to populate the config registry. +func ConfigFileUsed() string { return v.ConfigFileUsed() } + // 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) } + +// AddConfigPath adds a path for Viper to search for the config file in. +// Can be called multiple times to define multiple search paths. func (v *Viper) AddConfigPath(in string) { if in != "" { absin := absPathify(in) - jww.INFO.Println("adding", absin, "to paths to search") + v.logger.Infof("adding %s to paths to search", absin) if !stringInSlice(absin, v.configPaths) { v.configPaths = append(v.configPaths, absin) } @@ -482,6 +518,21 @@ func (v *Viper) isPathShadowedInAutoEnv(path []string) string { // // "a b c" func SetTypeByDefaultValue(enable bool) { v.SetTypeByDefaultValue(enable) } + +// 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 (v *Viper) SetTypeByDefaultValue(enable bool) { v.typeByDefValue = enable } @@ -499,6 +550,14 @@ func GetViper() *Viper { // // Get returns an interface. For a specific value use one of the Get____ methods. func Get(key string) interface{} { return v.Get(key) } + +// 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 (v *Viper) Get(key string) interface{} { lcaseKey := strings.ToLower(key) val := v.find(lcaseKey) @@ -507,7 +566,6 @@ func (v *Viper) Get(key string) interface{} { } 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) @@ -541,6 +599,9 @@ func (v *Viper) Get(key string) interface{} { // 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) } + +// Sub returns new Viper instance representing a sub tree of this instance. +// Sub is case-insensitive for a key. func (v *Viper) Sub(key string) *Viper { subv := New() data := v.Get(key) @@ -557,72 +618,96 @@ func (v *Viper) Sub(key string) *Viper { // GetString returns the value associated with the key as a string. func GetString(key string) string { return v.GetString(key) } + +// GetString returns the value associated with the key as a string. 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) } + +// GetBool returns the value associated with the key as a boolean. 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) } + +// GetInt returns the value associated with the key as an integer. func (v *Viper) GetInt(key string) int { return cast.ToInt(v.Get(key)) } // GetInt32 returns the value associated with the key as an integer. func GetInt32(key string) int32 { return v.GetInt32(key) } + +// GetInt32 returns the value associated with the key as an integer. func (v *Viper) GetInt32(key string) int32 { return cast.ToInt32(v.Get(key)) } // GetInt64 returns the value associated with the key as an integer. func GetInt64(key string) int64 { return v.GetInt64(key) } + +// GetInt64 returns the value associated with the key as an integer. 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) } + +// GetFloat64 returns the value associated with the key as a float64. 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) } + +// GetTime returns the value associated with the key as time. 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) } + +// GetDuration returns the value associated with the key as a duration. 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) } + +// GetStringSlice returns the value associated with the key as a slice of strings. 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) } + +// GetStringMap returns the value associated with the key as a map of interfaces. 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) } + +// GetStringMapString returns the value associated with the key as a map of strings. 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) } + +// GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings. func (v *Viper) GetStringMapStringSlice(key string) map[string][]string { return cast.ToStringMapStringSlice(v.Get(key)) } @@ -630,6 +715,9 @@ func (v *Viper) GetStringMapStringSlice(key string) map[string][]string { // GetSizeInBytes returns the size of the value associated with the given key // in bytes. func GetSizeInBytes(key string) uint { return v.GetSizeInBytes(key) } + +// GetSizeInBytes returns the size of the value associated with the given key +// in bytes. func (v *Viper) GetSizeInBytes(key string) uint { sizeStr := cast.ToString(v.Get(key)) return parseSizeInBytes(sizeStr) @@ -639,6 +727,8 @@ func (v *Viper) GetSizeInBytes(key string) uint { func UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConfigOption) error { return v.UnmarshalKey(key, rawVal, opts...) } + +// UnmarshalKey takes a single key and unmarshals it into a Struct. func (v *Viper) UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConfigOption) error { err := decode(v.Get(key), defaultDecoderConfig(rawVal, opts...)) @@ -656,6 +746,9 @@ func (v *Viper) UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConf func Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error { return v.Unmarshal(rawVal, opts...) } + +// Unmarshal unmarshals the config into a Struct. Make sure that the tags +// on the fields of the structure are properly set. func (v *Viper) Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error { err := decode(v.AllSettings(), defaultDecoderConfig(rawVal, opts...)) @@ -715,6 +808,9 @@ func (v *Viper) UnmarshalExact(rawVal interface{}) error { // 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) } + +// BindPFlags binds a full flag set to the configuration, using each flag's long +// name as the config key. func (v *Viper) BindPFlags(flags *pflag.FlagSet) error { return v.BindFlagValues(pflagValueSet{flags}) } @@ -726,6 +822,13 @@ func (v *Viper) BindPFlags(flags *pflag.FlagSet) error { // Viper.BindPFlag("port", serverCmd.Flags().Lookup("port")) // func BindPFlag(key string, flag *pflag.Flag) error { return v.BindPFlag(key, flag) } + +// 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 (v *Viper) BindPFlag(key string, flag *pflag.Flag) error { return v.BindFlagValue(key, pflagValue{flag}) } @@ -733,6 +836,9 @@ func (v *Viper) BindPFlag(key string, flag *pflag.Flag) error { // 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) } + +// BindFlagValues binds a full FlagValue set to the configuration, using each flag's long +// name as the config key. func (v *Viper) BindFlagValues(flags FlagValueSet) (err error) { flags.VisitAll(func(flag FlagValue) { if err = v.BindFlagValue(flag.Name(), flag); err != nil { @@ -749,6 +855,13 @@ func (v *Viper) BindFlagValues(flags FlagValueSet) (err error) { // Viper.BindFlagValue("port", serverCmd.Flags().Lookup("port")) // func BindFlagValue(key string, flag FlagValue) error { return v.BindFlagValue(key, flag) } + +// 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 (v *Viper) BindFlagValue(key string, flag FlagValue) error { if flag == nil { return fmt.Errorf("flag for %q is nil", key) @@ -762,6 +875,11 @@ func (v *Viper) BindFlagValue(key string, flag FlagValue) error { // 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...) } + +// 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 (v *Viper) BindEnv(input ...string) error { var key, envkey string if len(input) == 0 { @@ -917,6 +1035,9 @@ func readAsCSV(val string) ([]string, error) { // 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) } + +// IsSet checks to see if the key has been set in any of the data locations. +// IsSet is case-insensitive for a key. func (v *Viper) IsSet(key string) bool { lcaseKey := strings.ToLower(key) val := v.find(lcaseKey) @@ -926,6 +1047,9 @@ func (v *Viper) IsSet(key string) bool { // AutomaticEnv has Viper check ENV variables for all. // keys set in config, default & flags func AutomaticEnv() { v.AutomaticEnv() } + +// AutomaticEnv has Viper check ENV variables for all. +// keys set in config, default & flags func (v *Viper) AutomaticEnv() { v.automaticEnvApplied = true } @@ -934,13 +1058,20 @@ func (v *Viper) AutomaticEnv() { // Useful for mapping an environmental variable to a key that does // not match it. func SetEnvKeyReplacer(r *strings.Replacer) { v.SetEnvKeyReplacer(r) } + +// SetEnvKeyReplacer sets the strings.Replacer on the viper object +// Useful for mapping an environmental variable to a key that does +// not match it. func (v *Viper) SetEnvKeyReplacer(r *strings.Replacer) { v.envKeyReplacer = r } -// Aliases provide another accessor for the same key. +// RegisterAlias 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) } + +// RegisterAlias provide another accessor for the same key. +// This enables one to change a name without breaking the application func (v *Viper) RegisterAlias(alias string, key string) { v.registerAlias(alias, strings.ToLower(key)) } @@ -973,14 +1104,14 @@ func (v *Viper) registerAlias(alias string, key string) { v.aliases[alias] = key } } else { - jww.WARN.Println("Creating circular reference alias", alias, key, v.realKey(key)) + v.logger.Warningf("Creating circular reference alias %s key %s with realKey: %s", 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) + v.logger.Debugf("Alias key %s to: %s", key, newkey) return v.realKey(newkey) } return key @@ -988,6 +1119,8 @@ func (v *Viper) realKey(key string) string { // 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) } + +// InConfig checks to see if the given key (or an alias) is in the config file. func (v *Viper) InConfig(key string) bool { // if the requested key is an alias, then return the proper key key = v.realKey(key) @@ -1000,6 +1133,10 @@ func (v *Viper) InConfig(key string) bool { // 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) } + +// 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 (v *Viper) SetDefault(key string, value interface{}) { // If alias passed in, then set the proper default key = v.realKey(strings.ToLower(key)) @@ -1018,6 +1155,11 @@ func (v *Viper) SetDefault(key string, value interface{}) { // 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) } + +// Set sets the value for the key in the override register. +// 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 (v *Viper) Set(key string, value interface{}) { // If alias passed in, then set the proper override key = v.realKey(strings.ToLower(key)) @@ -1034,8 +1176,11 @@ func (v *Viper) Set(key string, value interface{}) { // 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() } + +// ReadInConfig will discover and load the configuration file from disk +// and key/value stores, searching in one of the defined paths. func (v *Viper) ReadInConfig() error { - jww.INFO.Println("Attempting to read in config file") + v.logger.Infof("Attempting to read in config file") filename, err := v.getConfigFile() if err != nil { return err @@ -1045,7 +1190,7 @@ func (v *Viper) ReadInConfig() error { return UnsupportedConfigError(v.getConfigType()) } - jww.DEBUG.Println("Reading file: ", filename) + v.logger.Debugf("eading file: %s", filename) file, err := afero.ReadFile(v.fs, filename) if err != nil { return err @@ -1064,8 +1209,10 @@ func (v *Viper) ReadInConfig() error { // MergeInConfig merges a new configuration with an existing config. func MergeInConfig() error { return v.MergeInConfig() } + +// MergeInConfig merges a new configuration with an existing config. func (v *Viper) MergeInConfig() error { - jww.INFO.Println("Attempting to merge in config file") + v.logger.Infof("Attempting to merge in config file") filename, err := v.getConfigFile() if err != nil { return err @@ -1086,6 +1233,9 @@ func (v *Viper) MergeInConfig() error { // 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) } + +// ReadConfig will read a configuration file, setting existing keys to nil if the +// key does not exist in the file. func (v *Viper) ReadConfig(in io.Reader) error { v.config = make(map[string]interface{}) return v.unmarshalReader(in, v.config) @@ -1093,6 +1243,8 @@ func (v *Viper) ReadConfig(in io.Reader) error { // MergeConfig merges a new configuration with an existing config. func MergeConfig(in io.Reader) error { return v.MergeConfig(in) } + +// MergeConfig merges a new configuration with an existing config. func (v *Viper) MergeConfig(in io.Reader) error { if v.config == nil { v.config = make(map[string]interface{}) @@ -1107,6 +1259,8 @@ func (v *Viper) MergeConfig(in io.Reader) error { // WriteConfig writes the current configuration to a file. func WriteConfig() error { return v.WriteConfig() } + +// WriteConfig writes the current configuration to a file. func (v *Viper) WriteConfig() error { filename, err := v.getConfigFile() if err != nil { @@ -1117,6 +1271,8 @@ func (v *Viper) WriteConfig() error { // SafeWriteConfig writes current configuration to file only if the file does not exist. func SafeWriteConfig() error { return v.SafeWriteConfig() } + +// SafeWriteConfig writes current configuration to file only if the file does not exist. func (v *Viper) SafeWriteConfig() error { filename, err := v.getConfigFile() if err != nil { @@ -1127,22 +1283,26 @@ func (v *Viper) SafeWriteConfig() error { // WriteConfigAs writes current configuration to a given filename. func WriteConfigAs(filename string) error { return v.WriteConfigAs(filename) } + +// WriteConfigAs writes current configuration to a given filename. func (v *Viper) WriteConfigAs(filename string) error { return v.writeConfig(filename, true) } // SafeWriteConfigAs writes current configuration to a given filename if it does not exist. func SafeWriteConfigAs(filename string) error { return v.SafeWriteConfigAs(filename) } + +// SafeWriteConfigAs writes current configuration to a given filename if it does not exist. func (v *Viper) SafeWriteConfigAs(filename string) error { return v.writeConfig(filename, false) } func writeConfig(filename string, force bool) error { return v.writeConfig(filename, force) } func (v *Viper) writeConfig(filename string, force bool) error { - jww.INFO.Println("Attempting to write configuration to file.") + v.logger.Infof("Attempting to write in config file") ext := filepath.Ext(filename) if len(ext) <= 1 { - return fmt.Errorf("Filename: %s requires valid extension.", filename) + return fmt.Errorf("filename: %s requires valid extension", filename) } configType := ext[1:] if !stringInSlice(configType, SupportedExts) { @@ -1158,7 +1318,7 @@ func (v *Viper) writeConfig(filename string, force bool) error { if _, err := os.Stat(filename); os.IsNotExist(err) { flags = os.O_WRONLY } else { - return fmt.Errorf("File: %s exists. Use WriteConfig to overwrite.", filename) + return fmt.Errorf("file: %s exists. Use WriteConfig to overwrite", filename) } } f, err := v.fs.OpenFile(filename, flags, os.FileMode(0644)) @@ -1255,7 +1415,7 @@ func mergeMaps( for sk, sv := range src { tk := keyExists(sk, tgt) if tk == "" { - jww.TRACE.Printf("tk=\"\", tgt[%s]=%v", sk, sv) + v.logger.Debugf("tk=\"\", tgt[%s]=%v", sk, sv) tgt[sk] = sv if itgt != nil { itgt[sk] = sv @@ -1265,7 +1425,7 @@ func mergeMaps( tv, ok := tgt[tk] if !ok { - jww.TRACE.Printf("tgt[%s] != ok, tgt[%s]=%v", tk, sk, sv) + v.logger.Debugf("tgt[%s] != ok, tgt[%s]=%v", tk, sk, sv) tgt[sk] = sv if itgt != nil { itgt[sk] = sv @@ -1276,27 +1436,24 @@ func mergeMaps( 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) + v.logger.Errorf("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) + v.logger.Debugf("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)") + v.logger.Debugf("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") + v.logger.Debugf("merging maps") mergeMaps(sv.(map[string]interface{}), ttv, nil) default: - jww.TRACE.Printf("setting value") + v.logger.Debugf("setting value") tgt[tk] = sv if itgt != nil { itgt[tk] = sv @@ -1315,6 +1472,9 @@ func (v *Viper) insensitiviseMaps() { // 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() } + +// AllKeys returns all keys holding a value, regardless of where they are set. +// Nested keys are returned with a v.keyDelim (= ".") separator func (v *Viper) AllKeys() []string { m := map[string]bool{} // add all paths, by order of descending priority to ensure correct shadowing @@ -1376,7 +1536,7 @@ func (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[string]interfac func (v *Viper) mergeFlatMap(shadow map[string]bool, m map[string]interface{}) map[string]bool { // scan keys outer: - for k, _ := range m { + for k := range m { path := strings.Split(k, v.keyDelim) // scan intermediate paths var parentKey string @@ -1395,6 +1555,8 @@ outer: // AllSettings merges all settings and returns them as a map[string]interface{}. func AllSettings() map[string]interface{} { return v.AllSettings() } + +// AllSettings merges all settings and returns them as a map[string]interface{}. 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 @@ -1416,6 +1578,8 @@ func (v *Viper) AllSettings() map[string]interface{} { // SetFs sets the filesystem to use to read configuration. func SetFs(fs afero.Fs) { v.SetFs(fs) } + +// SetFs sets the filesystem to use to read configuration. func (v *Viper) SetFs(fs afero.Fs) { v.fs = fs } @@ -1423,6 +1587,9 @@ func (v *Viper) SetFs(fs afero.Fs) { // SetConfigName sets name for the config file. // Does not include extension. func SetConfigName(in string) { v.SetConfigName(in) } + +// SetConfigName sets name for the config file. +// Does not include extension. func (v *Viper) SetConfigName(in string) { if in != "" { v.configName = in @@ -1430,19 +1597,7 @@ func (v *Viper) SetConfigName(in string) { } } -// 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 { @@ -1470,11 +1625,11 @@ func (v *Viper) getConfigFile() (string, error) { } func (v *Viper) searchInPath(in string) (filename string) { - jww.DEBUG.Println("Searching for config in ", in) + v.logger.Debugf("Searching for config in %s", in) for _, ext := range SupportedExts { - jww.DEBUG.Println("Checking for", filepath.Join(in, v.configName+"."+ext)) + v.logger.Debugf("Checking for %s", filepath.Join(in, v.configName+"."+ext)) if b, _ := exists(v.fs, filepath.Join(in, v.configName+"."+ext)); b { - jww.DEBUG.Println("Found: ", filepath.Join(in, v.configName+"."+ext)) + v.logger.Debugf("Found: %s", filepath.Join(in, v.configName+"."+ext)) return filepath.Join(in, v.configName+"."+ext) } } @@ -1485,7 +1640,7 @@ func (v *Viper) searchInPath(in string) (filename string) { // 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) + v.logger.Infof("Searching for config in %s", v.configPaths) for _, cp := range v.configPaths { file := v.searchInPath(cp) @@ -1499,6 +1654,9 @@ func (v *Viper) findConfigFile() (string, error) { // Debug prints all configuration registries for debugging // purposes. func Debug() { v.Debug() } + +// Debug prints all configuration registries for debugging +// purposes. func (v *Viper) Debug() { fmt.Printf("Aliases:\n%#v\n", v.aliases) fmt.Printf("Override:\n%#v\n", v.override) @@ -1508,3 +1666,96 @@ func (v *Viper) Debug() { fmt.Printf("Config:\n%#v\n", v.config) fmt.Printf("Defaults:\n%#v\n", v.defaults) } + +// Logger is implemented by any logging system that is used for standard logs. +type Logger interface { + Errorf(string, ...interface{}) + Warningf(string, ...interface{}) + Infof(string, ...interface{}) + Debugf(string, ...interface{}) +} + +// Errorf logs an ERROR log message to the logger specified in opts or to the +// global logger if no logger is specified in opts. +func (v *Viper) Errorf(format string, vIn ...interface{}) { + if v.logger == nil { + return + } + v.logger.Errorf(format, vIn...) +} + +// Infof logs an INFO message to the logger specified in opts. +func (v *Viper) Infof(format string, vIn ...interface{}) { + if v.logger == nil { + return + } + v.logger.Infof(format, vIn...) +} + +// Warningf logs a WARNING message to the logger specified in opts. +func (v *Viper) Warningf(format string, vIn ...interface{}) { + if v.logger == nil { + return + } + v.logger.Warningf(format, vIn...) +} + +// Debugf logs a DEBUG message to the logger specified in opts. +func (v *Viper) Debugf(format string, vIn ...interface{}) { + if v.logger == nil { + return + } + v.logger.Debugf(format, vIn...) +} + +type loggingLevel int + +const ( + // DEBUG debug log level + DEBUG loggingLevel = iota + // INFO log level + INFO + // WARNING log level + WARNING + // ERROR log level + ERROR +) + +// DefaultLog call inline log obj +type DefaultLog struct { + *log.Logger + level loggingLevel +} + +// DefaultLogger set default loagger call inline log +func DefaultLogger(level loggingLevel) *DefaultLog { + return &DefaultLog{Logger: log.New(os.Stderr, "viper ", log.LstdFlags), level: level} +} + +// Errorf for DefaultLog +func (l *DefaultLog) Errorf(f string, v ...interface{}) { + if l.level <= ERROR { + l.Printf("ERROR: "+f, v...) + } +} + +// Warningf for DefaultLog +func (l *DefaultLog) Warningf(f string, v ...interface{}) { + if l.level <= WARNING { + l.Printf("WARNING: "+f, v...) + } +} + +// Infof for DefaultLog +func (l *DefaultLog) Infof(f string, v ...interface{}) { + if l.level <= INFO { + l.Printf("INFO: "+f, v...) + } +} + +// Debugf for DefaultLog +func (l *DefaultLog) Debugf(f string, v ...interface{}) { + if l.level <= DEBUG { + l.Printf("DEBUG: "+f, v...) + } +} diff --git a/viper_test.go b/viper_test.go deleted file mode 100644 index 2b2f431..0000000 --- a/viper_test.go +++ /dev/null @@ -1,870 +0,0 @@ -// Copyright © 2014 Steve Francia . -// -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file. - -package viper - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "os" - "path" - "reflect" - "sort" - "strings" - "testing" - "time" - - "github.com/mitchellh/mapstructure" - "github.com/spf13/afero" - "github.com/spf13/cast" - - "github.com/spf13/pflag" - "github.com/stretchr/testify/assert" -) - -type testUnmarshalExtra struct { - Existing bool -} - -var jsonExample = []byte(`{ -"id": "0001", -"type": "donut", -"name": "Cake", -"ppu": 0.55, -"batters": { - "batter": [ - { "type": "Regular" }, - { "type": "Chocolate" }, - { "type": "Blueberry" }, - { "type": "Devil's Food" } - ] - } -}`) - -func initConfigs() { - Reset() - var r io.Reader - - SetConfigType("json") - r = bytes.NewReader(jsonExample) - unmarshalReader(r, v.config) -} - -func initConfig(typ, config string) { - Reset() - SetConfigType(typ) - r := strings.NewReader(config) - - if err := unmarshalReader(r, v.config); err != nil { - panic(err) - } -} - -func initJSON() { - Reset() - SetConfigType("json") - r := bytes.NewReader(jsonExample) - - unmarshalReader(r, v.config) -} - -// make directories for testing -func initDirs(t *testing.T) (string, string, func()) { - - var ( - testDirs = []string{`a a`, `b`, `c\c`, `D_`} - config = `improbable` - ) - - root, err := ioutil.TempDir("", "") - - cleanup := true - defer func() { - if cleanup { - os.Chdir("..") - os.RemoveAll(root) - } - }() - - assert.Nil(t, err) - - err = os.Chdir(root) - assert.Nil(t, err) - - for _, dir := range testDirs { - err = os.Mkdir(dir, 0750) - assert.Nil(t, err) - - err = ioutil.WriteFile( - path.Join(dir, config+".toml"), - []byte("key = \"value is "+dir+"\"\n"), - 0640) - assert.Nil(t, err) - } - - cleanup = false - return root, config, func() { - os.Chdir("..") - os.RemoveAll(root) - } -} - -//stubs for PFlag Values -type stringValue string - -func newStringValue(val string, p *string) *stringValue { - *p = val - return (*stringValue)(p) -} - -func (s *stringValue) Set(val string) error { - *s = stringValue(val) - return nil -} - -func (s *stringValue) Type() string { - return "string" -} - -func (s *stringValue) String() string { - return fmt.Sprintf("%s", *s) -} - -func TestOverrides(t *testing.T) { - Set("age", 40) - assert.Equal(t, 40, Get("age")) -} - -func TestDefaultPost(t *testing.T) { - assert.NotEqual(t, "NYC", Get("state")) - SetDefault("state", "NYC") - assert.Equal(t, "NYC", Get("state")) -} - -func TestAliases(t *testing.T) { - RegisterAlias("years", "age") - assert.Equal(t, 40, Get("years")) - Set("years", 45) - assert.Equal(t, 45, Get("age")) -} - -func TestAliasInConfigFile(t *testing.T) { - // the config file specifies "beard". If we make this an alias for - // "hasbeard", we still want the old config file to work with beard. - RegisterAlias("beard", "hasbeard") - assert.Equal(t, true, Get("hasbeard")) - Set("hasbeard", false) - assert.Equal(t, false, Get("beard")) -} - -func TestJSON(t *testing.T) { - initJSON() - assert.Equal(t, "0001", Get("id")) -} - -func TestEnv(t *testing.T) { - initJSON() - - BindEnv("id") - BindEnv("f", "FOOD") - - os.Setenv("ID", "13") - os.Setenv("FOOD", "apple") - os.Setenv("NAME", "crunk") - - assert.Equal(t, "13", Get("id")) - assert.Equal(t, "apple", Get("f")) - assert.Equal(t, "Cake", Get("name")) - - AutomaticEnv() - - assert.Equal(t, "crunk", Get("name")) - -} - -func TestEnvPrefix(t *testing.T) { - initJSON() - - SetEnvPrefix("foo") // will be uppercased automatically - BindEnv("id") - BindEnv("f", "FOOD") // not using prefix - - os.Setenv("FOO_ID", "13") - os.Setenv("FOOD", "apple") - os.Setenv("FOO_NAME", "crunk") - - assert.Equal(t, "13", Get("id")) - assert.Equal(t, "apple", Get("f")) - assert.Equal(t, "Cake", Get("name")) - - AutomaticEnv() - - assert.Equal(t, "crunk", Get("name")) -} - -func TestAutoEnv(t *testing.T) { - Reset() - - AutomaticEnv() - os.Setenv("FOO_BAR", "13") - assert.Equal(t, "13", Get("foo_bar")) -} - -func TestAutoEnvWithPrefix(t *testing.T) { - Reset() - - AutomaticEnv() - SetEnvPrefix("Baz") - os.Setenv("BAZ_BAR", "13") - assert.Equal(t, "13", Get("bar")) -} - -func TestSetEnvKeyReplacer(t *testing.T) { - Reset() - - AutomaticEnv() - os.Setenv("REFRESH_INTERVAL", "30s") - - replacer := strings.NewReplacer("-", "_") - SetEnvKeyReplacer(replacer) - - assert.Equal(t, "30s", Get("refresh-interval")) -} - -func TestAllKeys(t *testing.T) { - initConfigs() - - ks := sort.StringSlice{"title", "newkey", "owner.organization", "owner.dob", "owner.bio", "name", "beard", "ppu", "batters.batter", "hobbies", "clothing.jacket", "clothing.trousers", "clothing.pants.size", "age", "hacker", "id", "type", "eyes", "p_id", "p_ppu", "p_batters.batter.type", "p_type", "p_name", "foos"} - dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z") - all := map[string]interface{}{"owner": map[string]interface{}{"organization": "MongoDB", "bio": "MongoDB Chief Developer Advocate & Hacker at Large", "dob": dob}, "title": "TOML Example", "ppu": 0.55, "eyes": "brown", "clothing": map[string]interface{}{"trousers": "denim", "jacket": "leather", "pants": map[string]interface{}{"size": "large"}}, "id": "0001", "batters": map[string]interface{}{"batter": []interface{}{map[string]interface{}{"type": "Regular"}, map[string]interface{}{"type": "Chocolate"}, map[string]interface{}{"type": "Blueberry"}, map[string]interface{}{"type": "Devil's Food"}}}, "hacker": true, "beard": true, "hobbies": []interface{}{"skateboarding", "snowboarding", "go"}, "age": 35, "type": "donut", "newkey": "remote", "name": "Cake", "p_id": "0001", "p_ppu": "0.55", "p_name": "Cake", "p_batters": map[string]interface{}{"batter": map[string]interface{}{"type": "Regular"}}, "p_type": "donut", "foos": []map[string]interface{}{map[string]interface{}{"foo": []map[string]interface{}{map[string]interface{}{"key": 1}, map[string]interface{}{"key": 2}, map[string]interface{}{"key": 3}, map[string]interface{}{"key": 4}}}}} - - var allkeys sort.StringSlice - allkeys = AllKeys() - allkeys.Sort() - ks.Sort() - - assert.Equal(t, ks, allkeys) - assert.Equal(t, all, AllSettings()) -} - -func TestAllKeysWithEnv(t *testing.T) { - v := New() - - // bind and define environment variables (including a nested one) - v.BindEnv("id") - v.BindEnv("foo.bar") - v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) - os.Setenv("ID", "13") - os.Setenv("FOO_BAR", "baz") - - expectedKeys := sort.StringSlice{"id", "foo.bar"} - expectedKeys.Sort() - keys := sort.StringSlice(v.AllKeys()) - keys.Sort() - assert.Equal(t, expectedKeys, keys) -} - -func TestAliasesOfAliases(t *testing.T) { - Set("Title", "Checking Case") - RegisterAlias("Foo", "Bar") - RegisterAlias("Bar", "Title") - assert.Equal(t, "Checking Case", Get("FOO")) -} - -func TestRecursiveAliases(t *testing.T) { - RegisterAlias("Baz", "Roo") - RegisterAlias("Roo", "baz") -} - -func TestUnmarshal(t *testing.T) { - SetDefault("port", 1313) - Set("name", "Steve") - Set("duration", "1s1ms") - - type config struct { - Port int - Name string - Duration time.Duration - } - - var C config - - err := Unmarshal(&C) - if err != nil { - t.Fatalf("unable to decode into struct, %v", err) - } - - assert.Equal(t, &config{Name: "Steve", Port: 1313, Duration: time.Second + time.Millisecond}, &C) - - Set("port", 1234) - err = Unmarshal(&C) - if err != nil { - t.Fatalf("unable to decode into struct, %v", err) - } - assert.Equal(t, &config{Name: "Steve", Port: 1234, Duration: time.Second + time.Millisecond}, &C) -} - -func TestUnmarshalWithDecoderOptions(t *testing.T) { - Set("credentials", "{\"foo\":\"bar\"}") - - opt := DecodeHook(mapstructure.ComposeDecodeHookFunc( - mapstructure.StringToTimeDurationHookFunc(), - mapstructure.StringToSliceHookFunc(","), - // Custom Decode Hook Function - func(rf reflect.Kind, rt reflect.Kind, data interface{}) (interface{}, error) { - if rf != reflect.String || rt != reflect.Map { - return data, nil - } - m := map[string]string{} - raw := data.(string) - if raw == "" { - return m, nil - } - return m, json.Unmarshal([]byte(raw), &m) - }, - )) - - type config struct { - Credentials map[string]string - } - - var C config - - err := Unmarshal(&C, opt) - if err != nil { - t.Fatalf("unable to decode into struct, %v", err) - } - - assert.Equal(t, &config{ - Credentials: map[string]string{"foo": "bar"}, - }, &C) -} - -func TestBindPFlags(t *testing.T) { - v := New() // create independent Viper object - flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError) - - var testValues = map[string]*string{ - "host": nil, - "port": nil, - "endpoint": nil, - } - - var mutatedTestValues = map[string]string{ - "host": "localhost", - "port": "6060", - "endpoint": "/public", - } - - for name := range testValues { - testValues[name] = flagSet.String(name, "", "test") - } - - err := v.BindPFlags(flagSet) - if err != nil { - t.Fatalf("error binding flag set, %v", err) - } - - flagSet.VisitAll(func(flag *pflag.Flag) { - flag.Value.Set(mutatedTestValues[flag.Name]) - flag.Changed = true - }) - - for name, expected := range mutatedTestValues { - assert.Equal(t, expected, v.Get(name)) - } - -} - -func TestBindPFlagsStringSlice(t *testing.T) { - for _, testValue := range []struct { - Expected []string - Value string - }{ - {[]string{}, ""}, - {[]string{"jeden"}, "jeden"}, - {[]string{"dwa", "trzy"}, "dwa,trzy"}, - {[]string{"cztery", "piec , szesc"}, "cztery,\"piec , szesc\""}} { - - for _, changed := range []bool{true, false} { - v := New() // create independent Viper object - flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError) - flagSet.StringSlice("stringslice", testValue.Expected, "test") - flagSet.Visit(func(f *pflag.Flag) { - if len(testValue.Value) > 0 { - f.Value.Set(testValue.Value) - f.Changed = changed - } - }) - - err := v.BindPFlags(flagSet) - if err != nil { - t.Fatalf("error binding flag set, %v", err) - } - - type TestStr struct { - StringSlice []string - } - val := &TestStr{} - if err := v.Unmarshal(val); err != nil { - t.Fatalf("%+#v cannot unmarshal: %s", testValue.Value, err) - } - assert.Equal(t, testValue.Expected, val.StringSlice) - } - } -} - -func TestBindPFlag(t *testing.T) { - var testString = "testing" - var testValue = newStringValue(testString, &testString) - - flag := &pflag.Flag{ - Name: "testflag", - Value: testValue, - Changed: false, - } - - BindPFlag("testvalue", flag) - - assert.Equal(t, testString, Get("testvalue")) - - flag.Value.Set("testing_mutate") - flag.Changed = true //hack for pflag usage - - assert.Equal(t, "testing_mutate", Get("testvalue")) - -} - -func TestBoundCaseSensitivity(t *testing.T) { - assert.Equal(t, "brown", Get("eyes")) - - BindEnv("eYEs", "TURTLE_EYES") - os.Setenv("TURTLE_EYES", "blue") - - assert.Equal(t, "blue", Get("eyes")) - - var testString = "green" - var testValue = newStringValue(testString, &testString) - - flag := &pflag.Flag{ - Name: "eyeballs", - Value: testValue, - Changed: true, - } - - BindPFlag("eYEs", flag) - assert.Equal(t, "green", Get("eyes")) - -} - -func TestSizeInBytes(t *testing.T) { - input := map[string]uint{ - "": 0, - "b": 0, - "12 bytes": 0, - "200000000000gb": 0, - "12 b": 12, - "43 MB": 43 * (1 << 20), - "10mb": 10 * (1 << 20), - "1gb": 1 << 30, - } - - for str, expected := range input { - assert.Equal(t, expected, parseSizeInBytes(str), str) - } -} - -func TestFindsNestedKeys(t *testing.T) { - initConfigs() - dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z") - - Set("super", map[string]interface{}{ - "deep": map[string]interface{}{ - "nested": "value", - }, - }) - - expected := map[string]interface{}{ - "super": map[string]interface{}{ - "deep": map[string]interface{}{ - "nested": "value", - }, - }, - "super.deep": map[string]interface{}{ - "nested": "value", - }, - "super.deep.nested": "value", - "owner.organization": "MongoDB", - "batters.batter": []interface{}{ - map[string]interface{}{ - "type": "Regular", - }, - map[string]interface{}{ - "type": "Chocolate", - }, - map[string]interface{}{ - "type": "Blueberry", - }, - map[string]interface{}{ - "type": "Devil's Food", - }, - }, - "hobbies": []interface{}{ - "skateboarding", "snowboarding", "go", - }, - "title": "TOML Example", - "newkey": "remote", - "batters": map[string]interface{}{ - "batter": []interface{}{ - map[string]interface{}{ - "type": "Regular", - }, - map[string]interface{}{ - "type": "Chocolate", - }, map[string]interface{}{ - "type": "Blueberry", - }, map[string]interface{}{ - "type": "Devil's Food", - }, - }, - }, - "eyes": "brown", - "age": 35, - "owner": map[string]interface{}{ - "organization": "MongoDB", - "bio": "MongoDB Chief Developer Advocate & Hacker at Large", - "dob": dob, - }, - "owner.bio": "MongoDB Chief Developer Advocate & Hacker at Large", - "type": "donut", - "id": "0001", - "name": "Cake", - "hacker": true, - "ppu": 0.55, - "clothing": map[string]interface{}{ - "jacket": "leather", - "trousers": "denim", - "pants": map[string]interface{}{ - "size": "large", - }, - }, - "clothing.jacket": "leather", - "clothing.pants.size": "large", - "clothing.trousers": "denim", - "owner.dob": dob, - "beard": true, - "foos": []map[string]interface{}{ - map[string]interface{}{ - "foo": []map[string]interface{}{ - map[string]interface{}{ - "key": 1, - }, - map[string]interface{}{ - "key": 2, - }, - map[string]interface{}{ - "key": 3, - }, - map[string]interface{}{ - "key": 4, - }, - }, - }, - }, - } - - for key, expectedValue := range expected { - - assert.Equal(t, expectedValue, v.Get(key)) - } - -} - -func TestDirsSearch(t *testing.T) { - - root, config, cleanup := initDirs(t) - defer cleanup() - - v := New() - v.SetConfigName(config) - v.SetDefault(`key`, `default`) - - entries, err := ioutil.ReadDir(root) - for _, e := range entries { - if e.IsDir() { - v.AddConfigPath(e.Name()) - } - } - - err = v.ReadInConfig() - assert.Nil(t, err) - - assert.Equal(t, `value is `+path.Base(v.configPaths[0]), v.GetString(`key`)) -} - -func TestWrongDirsSearchNotFound(t *testing.T) { - - _, config, cleanup := initDirs(t) - defer cleanup() - - v := New() - v.SetConfigName(config) - v.SetDefault(`key`, `default`) - - v.AddConfigPath(`whattayoutalkingbout`) - v.AddConfigPath(`thispathaintthere`) - - err := v.ReadInConfig() - assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err)) - - // Even though config did not load and the error might have - // been ignored by the client, the default still loads - assert.Equal(t, `default`, v.GetString(`key`)) -} - -func TestWrongDirsSearchNotFoundForMerge(t *testing.T) { - - _, config, cleanup := initDirs(t) - defer cleanup() - - v := New() - v.SetConfigName(config) - v.SetDefault(`key`, `default`) - - v.AddConfigPath(`whattayoutalkingbout`) - v.AddConfigPath(`thispathaintthere`) - - err := v.MergeInConfig() - assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err)) - - // Even though config did not load and the error might have - // been ignored by the client, the default still loads - assert.Equal(t, `default`, v.GetString(`key`)) -} - -var jsonWriteExpected = []byte(`{ - "batters": { - "batter": [ - { - "type": "Regular" - }, - { - "type": "Chocolate" - }, - { - "type": "Blueberry" - }, - { - "type": "Devil's Food" - } - ] - }, - "id": "0001", - "name": "Cake", - "ppu": 0.55, - "type": "donut" -}`) - -func TestWriteConfigJson(t *testing.T) { - v := New() - fs := afero.NewMemMapFs() - v.SetFs(fs) - v.SetConfigName("c") - v.SetConfigType("json") - err := v.ReadConfig(bytes.NewBuffer(jsonExample)) - if err != nil { - t.Fatal(err) - } - if err := v.WriteConfigAs("c.json"); err != nil { - t.Fatal(err) - } - read, err := afero.ReadFile(fs, "c.json") - if err != nil { - t.Fatal(err) - } - assert.Equal(t, jsonWriteExpected, read) -} - -var propertiesWriteExpected = []byte(`p_id = 0001 -p_type = donut -p_name = Cake -p_ppu = 0.55 -p_batters.batter.type = Regular -`) - -func TestUnmarshalingWithAliases(t *testing.T) { - v := New() - v.SetDefault("ID", 1) - v.Set("name", "Steve") - v.Set("lastname", "Owen") - - v.RegisterAlias("UserID", "ID") - v.RegisterAlias("Firstname", "name") - v.RegisterAlias("Surname", "lastname") - - type config struct { - ID int - FirstName string - Surname string - } - - var C config - err := v.Unmarshal(&C) - if err != nil { - t.Fatalf("unable to decode into struct, %v", err) - } - - assert.Equal(t, &config{ID: 1, FirstName: "Steve", Surname: "Owen"}, &C) -} - -func TestShadowedNestedValue(t *testing.T) { - - config := `name: steve -clothing: - jacket: leather - trousers: denim - pants: - size: large -` - initConfig("yaml", config) - - assert.Equal(t, "steve", GetString("name")) - - polyester := "polyester" - SetDefault("clothing.shirt", polyester) - SetDefault("clothing.jacket.price", 100) - - assert.Equal(t, "leather", GetString("clothing.jacket")) - assert.Nil(t, Get("clothing.jacket.price")) - assert.Equal(t, polyester, GetString("clothing.shirt")) - - clothingSettings := AllSettings()["clothing"].(map[string]interface{}) - assert.Equal(t, "leather", clothingSettings["jacket"]) - assert.Equal(t, polyester, clothingSettings["shirt"]) -} - -func TestDotParameter(t *testing.T) { - initJSON() - // shoud take precedence over batters defined in jsonExample - r := bytes.NewReader([]byte(`{ "batters.batter": [ { "type": "Small" } ] }`)) - unmarshalReader(r, v.config) - - actual := Get("batters.batter") - expected := []interface{}{map[string]interface{}{"type": "Small"}} - assert.Equal(t, expected, actual) -} - -func TestCaseInsensitiveSet(t *testing.T) { - Reset() - m1 := map[string]interface{}{ - "Foo": 32, - "Bar": map[interface{}]interface { - }{ - "ABc": "A", - "cDE": "B"}, - } - - m2 := map[string]interface{}{ - "Foo": 52, - "Bar": map[interface{}]interface { - }{ - "bCd": "A", - "eFG": "B"}, - } - - Set("Given1", m1) - Set("Number1", 42) - - SetDefault("Given2", m2) - SetDefault("Number2", 52) - - // Verify SetDefault - if v := Get("number2"); v != 52 { - t.Fatalf("Expected 52 got %q", v) - } - - if v := Get("given2.foo"); v != 52 { - t.Fatalf("Expected 52 got %q", v) - } - - if v := Get("given2.bar.bcd"); v != "A" { - t.Fatalf("Expected A got %q", v) - } - - if _, ok := m2["Foo"]; !ok { - t.Fatal("Input map changed") - } - - // Verify Set - if v := Get("number1"); v != 42 { - t.Fatalf("Expected 42 got %q", v) - } - - if v := Get("given1.foo"); v != 32 { - t.Fatalf("Expected 32 got %q", v) - } - - if v := Get("given1.bar.abc"); v != "A" { - t.Fatalf("Expected A got %q", v) - } - - if _, ok := m1["Foo"]; !ok { - t.Fatal("Input map changed") - } -} - -func doTestCaseInsensitive(t *testing.T, typ, config string) { - initConfig(typ, config) - Set("RfD", true) - assert.Equal(t, true, Get("rfd")) - assert.Equal(t, true, Get("rFD")) - assert.Equal(t, 1, cast.ToInt(Get("abcd"))) - assert.Equal(t, 1, cast.ToInt(Get("Abcd"))) - assert.Equal(t, 2, cast.ToInt(Get("ef.gh"))) - assert.Equal(t, 3, cast.ToInt(Get("ef.ijk"))) - assert.Equal(t, 4, cast.ToInt(Get("ef.lm.no"))) - assert.Equal(t, 5, cast.ToInt(Get("ef.lm.p.q"))) - -} - -func BenchmarkGetBool(b *testing.B) { - key := "BenchmarkGetBool" - v = New() - v.Set(key, true) - - for i := 0; i < b.N; i++ { - if !v.GetBool(key) { - b.Fatal("GetBool returned false") - } - } -} - -func BenchmarkGet(b *testing.B) { - key := "BenchmarkGet" - v = New() - v.Set(key, true) - - for i := 0; i < b.N; i++ { - if !v.Get(key).(bool) { - b.Fatal("Get returned false") - } - } -} - -// This is the "perfect result" for the above. -func BenchmarkGetBoolFromMap(b *testing.B) { - m := make(map[string]bool) - key := "BenchmarkGetBool" - m[key] = true - - for i := 0; i < b.N; i++ { - if !m[key] { - b.Fatal("Map value was false") - } - } -}