mirror of
https://github.com/spf13/viper
synced 2025-05-06 04:07:17 +00:00
remove test
add logger for viper
This commit is contained in:
parent
4317f4793e
commit
bd4eb4285b
9 changed files with 333 additions and 1221 deletions
2
flags.go
2
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
|
||||
}
|
||||
|
|
|
@ -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"))
|
||||
}
|
16
go.mod
16
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
|
||||
)
|
||||
|
|
25
go.sum
25
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=
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
6
util.go
6
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 ""
|
||||
}
|
||||
|
||||
|
|
54
util_test.go
54
util_test.go
|
@ -1,54 +0,0 @@
|
|||
// Copyright © 2016 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.
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
343
viper.go
343
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...)
|
||||
}
|
||||
}
|
||||
|
|
870
viper_test.go
870
viper_test.go
|
@ -1,870 +0,0 @@
|
|||
// 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.
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue