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
|
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 {
|
func (p pflagValue) HasChanged() bool {
|
||||||
return p.flag.Changed
|
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
|
module github.com/xurwxj/viper
|
||||||
|
|
||||||
|
go 1.15
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fsnotify/fsnotify v1.4.7
|
github.com/fsnotify/fsnotify v1.4.9
|
||||||
github.com/mitchellh/mapstructure v1.0.0
|
github.com/mitchellh/mapstructure v1.3.3
|
||||||
github.com/spf13/afero v1.1.2
|
github.com/spf13/afero v1.3.5
|
||||||
github.com/spf13/cast v1.2.0
|
github.com/spf13/cast v1.3.1
|
||||||
github.com/spf13/jwalterweatherman v1.0.0
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/spf13/pflag v1.0.2
|
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f // indirect
|
||||||
golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992 // indirect
|
|
||||||
golang.org/x/text v0.3.0 // 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/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 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
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 h1:vVpGvMXJPqSDh2VYHF7gsfQj8Ncx+Xw5Y1KHeTRY+7I=
|
||||||
github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
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 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
|
||||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
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 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg=
|
||||||
github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
|
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 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
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 h1:Fy0orTDgHdbnzHcsOgfCN4LtHf0ec3wwtiwJqwvf3Gc=
|
||||||
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
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 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-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 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
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/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/afero"
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
jww "github.com/spf13/jwalterweatherman"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConfigParseError denotes failing to parse configuration file.
|
// ConfigParseError denotes failing to parse configuration file.
|
||||||
|
@ -89,7 +88,7 @@ func insensitiviseMap(m map[string]interface{}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func absPathify(inPath string) string {
|
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") {
|
if strings.HasPrefix(inPath, "$HOME") {
|
||||||
inPath = userHomeDir() + inPath[5:]
|
inPath = userHomeDir() + inPath[5:]
|
||||||
|
@ -109,8 +108,7 @@ func absPathify(inPath string) string {
|
||||||
return filepath.Clean(p)
|
return filepath.Clean(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
jww.ERROR.Println("Couldn't discover absolute path")
|
fmt.Println("Couldn't discover absolute path", err)
|
||||||
jww.ERROR.Println(err)
|
|
||||||
return ""
|
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/mitchellh/mapstructure"
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
jww "github.com/spf13/jwalterweatherman"
|
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -141,8 +140,8 @@ type Viper struct {
|
||||||
// Name of file to look for inside the path
|
// Name of file to look for inside the path
|
||||||
configName string
|
configName string
|
||||||
configFile string
|
configFile string
|
||||||
configType string
|
|
||||||
envPrefix string
|
envPrefix string
|
||||||
|
logger Logger
|
||||||
|
|
||||||
automaticEnvApplied bool
|
automaticEnvApplied bool
|
||||||
envKeyReplacer *strings.Replacer
|
envKeyReplacer *strings.Replacer
|
||||||
|
@ -172,12 +171,13 @@ func New() *Viper {
|
||||||
v.pflags = make(map[string]FlagValue)
|
v.pflags = make(map[string]FlagValue)
|
||||||
v.env = make(map[string]string)
|
v.env = make(map[string]string)
|
||||||
v.aliases = make(map[string]string)
|
v.aliases = make(map[string]string)
|
||||||
|
v.logger = DefaultLogger(INFO)
|
||||||
v.typeByDefValue = false
|
v.typeByDefValue = false
|
||||||
|
|
||||||
return v
|
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
|
// In the public interface for the viper package so applications
|
||||||
// can use it in their testing as well.
|
// can use it in their testing as well.
|
||||||
func Reset() {
|
func Reset() {
|
||||||
|
@ -188,26 +188,31 @@ func Reset() {
|
||||||
// SupportedExts are universally supported extensions.
|
// SupportedExts are universally supported extensions.
|
||||||
var SupportedExts = []string{"json"}
|
var SupportedExts = []string{"json"}
|
||||||
|
|
||||||
|
// OnConfigChange are check change event
|
||||||
func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) }
|
func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) }
|
||||||
|
|
||||||
|
// OnConfigChange are check change event
|
||||||
func (v *Viper) OnConfigChange(run func(in fsnotify.Event)) {
|
func (v *Viper) OnConfigChange(run func(in fsnotify.Event)) {
|
||||||
v.onConfigChange = run
|
v.onConfigChange = run
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WatchConfig watch config change
|
||||||
func WatchConfig() { v.WatchConfig() }
|
func WatchConfig() { v.WatchConfig() }
|
||||||
|
|
||||||
|
// WatchConfig watch config change
|
||||||
func (v *Viper) WatchConfig() {
|
func (v *Viper) WatchConfig() {
|
||||||
initWG := sync.WaitGroup{}
|
initWG := sync.WaitGroup{}
|
||||||
initWG.Add(1)
|
initWG.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
watcher, err := fsnotify.NewWatcher()
|
watcher, err := fsnotify.NewWatcher()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
v.logger.Errorf("fsnotify watcher err: %v", err)
|
||||||
}
|
}
|
||||||
defer watcher.Close()
|
defer watcher.Close()
|
||||||
// we have to watch the entire directory to pick up renames/atomic saves in a cross-platform way
|
// we have to watch the entire directory to pick up renames/atomic saves in a cross-platform way
|
||||||
filename, err := v.getConfigFile()
|
filename, err := v.getConfigFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("error: %v\n", err)
|
v.logger.Errorf("getConfigFile error: %v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,7 +241,7 @@ func (v *Viper) WatchConfig() {
|
||||||
realConfigFile = currentConfigFile
|
realConfigFile = currentConfigFile
|
||||||
err := v.ReadInConfig()
|
err := v.ReadInConfig()
|
||||||
if err != nil {
|
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 {
|
if v.onConfigChange != nil {
|
||||||
v.onConfigChange(event)
|
v.onConfigChange(event)
|
||||||
|
@ -249,7 +254,7 @@ func (v *Viper) WatchConfig() {
|
||||||
|
|
||||||
case err, ok := <-watcher.Errors:
|
case err, ok := <-watcher.Errors:
|
||||||
if ok { // 'Errors' channel is not closed
|
if ok { // 'Errors' channel is not closed
|
||||||
log.Printf("watcher error: %v\n", err)
|
v.logger.Errorf("watcher error: %v\n", err)
|
||||||
}
|
}
|
||||||
eventsWG.Done()
|
eventsWG.Done()
|
||||||
return
|
return
|
||||||
|
@ -266,6 +271,9 @@ func (v *Viper) WatchConfig() {
|
||||||
// SetConfigFile explicitly defines the path, name and extension of the config file.
|
// SetConfigFile explicitly defines the path, name and extension of the config file.
|
||||||
// Viper will use this and not check any of the config paths.
|
// Viper will use this and not check any of the config paths.
|
||||||
func SetConfigFile(in string) { v.SetConfigFile(in) }
|
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) {
|
func (v *Viper) SetConfigFile(in string) {
|
||||||
if in != "" {
|
if in != "" {
|
||||||
v.configFile = 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
|
// E.g. if your prefix is "spf", the env registry will look for env
|
||||||
// variables that start with "SPF_".
|
// variables that start with "SPF_".
|
||||||
func SetEnvPrefix(in string) { v.SetEnvPrefix(in) }
|
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) {
|
func (v *Viper) SetEnvPrefix(in string) {
|
||||||
if in != "" {
|
if in != "" {
|
||||||
v.envPrefix = in
|
v.envPrefix = in
|
||||||
|
@ -304,17 +316,41 @@ func (v *Viper) getEnv(key string) string {
|
||||||
return os.Getenv(key)
|
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.
|
// ConfigFileUsed returns the file used to populate the config registry.
|
||||||
func ConfigFileUsed() string { return v.ConfigFileUsed() }
|
|
||||||
func (v *Viper) ConfigFileUsed() string { return v.configFile }
|
func (v *Viper) ConfigFileUsed() string { return v.configFile }
|
||||||
|
|
||||||
// AddConfigPath adds a path for Viper to search for the config file in.
|
// AddConfigPath adds a path for Viper to search for the config file in.
|
||||||
// Can be called multiple times to define multiple search paths.
|
// Can be called multiple times to define multiple search paths.
|
||||||
func AddConfigPath(in string) { v.AddConfigPath(in) }
|
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) {
|
func (v *Viper) AddConfigPath(in string) {
|
||||||
if in != "" {
|
if in != "" {
|
||||||
absin := absPathify(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) {
|
if !stringInSlice(absin, v.configPaths) {
|
||||||
v.configPaths = append(v.configPaths, absin)
|
v.configPaths = append(v.configPaths, absin)
|
||||||
}
|
}
|
||||||
|
@ -482,6 +518,21 @@ func (v *Viper) isPathShadowedInAutoEnv(path []string) string {
|
||||||
//
|
//
|
||||||
// "a b c"
|
// "a b c"
|
||||||
func SetTypeByDefaultValue(enable bool) { v.SetTypeByDefaultValue(enable) }
|
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) {
|
func (v *Viper) SetTypeByDefaultValue(enable bool) {
|
||||||
v.typeByDefValue = enable
|
v.typeByDefValue = enable
|
||||||
}
|
}
|
||||||
|
@ -499,6 +550,14 @@ func GetViper() *Viper {
|
||||||
//
|
//
|
||||||
// Get returns an interface. For a specific value use one of the Get____ methods.
|
// Get returns an interface. For a specific value use one of the Get____ methods.
|
||||||
func Get(key string) interface{} { return v.Get(key) }
|
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{} {
|
func (v *Viper) Get(key string) interface{} {
|
||||||
lcaseKey := strings.ToLower(key)
|
lcaseKey := strings.ToLower(key)
|
||||||
val := v.find(lcaseKey)
|
val := v.find(lcaseKey)
|
||||||
|
@ -507,7 +566,6 @@ func (v *Viper) Get(key string) interface{} {
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.typeByDefValue {
|
if v.typeByDefValue {
|
||||||
// TODO(bep) this branch isn't covered by a single test.
|
|
||||||
valType := val
|
valType := val
|
||||||
path := strings.Split(lcaseKey, v.keyDelim)
|
path := strings.Split(lcaseKey, v.keyDelim)
|
||||||
defVal := v.searchMap(v.defaults, path)
|
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 returns new Viper instance representing a sub tree of this instance.
|
||||||
// Sub is case-insensitive for a key.
|
// Sub is case-insensitive for a key.
|
||||||
func Sub(key string) *Viper { return v.Sub(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 {
|
func (v *Viper) Sub(key string) *Viper {
|
||||||
subv := New()
|
subv := New()
|
||||||
data := v.Get(key)
|
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.
|
// GetString returns the value associated with the key as a string.
|
||||||
func GetString(key string) string { return v.GetString(key) }
|
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 {
|
func (v *Viper) GetString(key string) string {
|
||||||
return cast.ToString(v.Get(key))
|
return cast.ToString(v.Get(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBool returns the value associated with the key as a boolean.
|
// GetBool returns the value associated with the key as a boolean.
|
||||||
func GetBool(key string) bool { return v.GetBool(key) }
|
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 {
|
func (v *Viper) GetBool(key string) bool {
|
||||||
return cast.ToBool(v.Get(key))
|
return cast.ToBool(v.Get(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInt returns the value associated with the key as an integer.
|
// GetInt returns the value associated with the key as an integer.
|
||||||
func GetInt(key string) int { return v.GetInt(key) }
|
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 {
|
func (v *Viper) GetInt(key string) int {
|
||||||
return cast.ToInt(v.Get(key))
|
return cast.ToInt(v.Get(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInt32 returns the value associated with the key as an integer.
|
// GetInt32 returns the value associated with the key as an integer.
|
||||||
func GetInt32(key string) int32 { return v.GetInt32(key) }
|
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 {
|
func (v *Viper) GetInt32(key string) int32 {
|
||||||
return cast.ToInt32(v.Get(key))
|
return cast.ToInt32(v.Get(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInt64 returns the value associated with the key as an integer.
|
// GetInt64 returns the value associated with the key as an integer.
|
||||||
func GetInt64(key string) int64 { return v.GetInt64(key) }
|
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 {
|
func (v *Viper) GetInt64(key string) int64 {
|
||||||
return cast.ToInt64(v.Get(key))
|
return cast.ToInt64(v.Get(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFloat64 returns the value associated with the key as a float64.
|
// GetFloat64 returns the value associated with the key as a float64.
|
||||||
func GetFloat64(key string) float64 { return v.GetFloat64(key) }
|
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 {
|
func (v *Viper) GetFloat64(key string) float64 {
|
||||||
return cast.ToFloat64(v.Get(key))
|
return cast.ToFloat64(v.Get(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTime returns the value associated with the key as time.
|
// GetTime returns the value associated with the key as time.
|
||||||
func GetTime(key string) time.Time { return v.GetTime(key) }
|
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 {
|
func (v *Viper) GetTime(key string) time.Time {
|
||||||
return cast.ToTime(v.Get(key))
|
return cast.ToTime(v.Get(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDuration returns the value associated with the key as a duration.
|
// GetDuration returns the value associated with the key as a duration.
|
||||||
func GetDuration(key string) time.Duration { return v.GetDuration(key) }
|
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 {
|
func (v *Viper) GetDuration(key string) time.Duration {
|
||||||
return cast.ToDuration(v.Get(key))
|
return cast.ToDuration(v.Get(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStringSlice returns the value associated with the key as a slice of strings.
|
// GetStringSlice returns the value associated with the key as a slice of strings.
|
||||||
func GetStringSlice(key string) []string { return v.GetStringSlice(key) }
|
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 {
|
func (v *Viper) GetStringSlice(key string) []string {
|
||||||
return cast.ToStringSlice(v.Get(key))
|
return cast.ToStringSlice(v.Get(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStringMap returns the value associated with the key as a map of interfaces.
|
// GetStringMap returns the value associated with the key as a map of interfaces.
|
||||||
func GetStringMap(key string) map[string]interface{} { return v.GetStringMap(key) }
|
func 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{} {
|
func (v *Viper) GetStringMap(key string) map[string]interface{} {
|
||||||
return cast.ToStringMap(v.Get(key))
|
return cast.ToStringMap(v.Get(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStringMapString returns the value associated with the key as a map of strings.
|
// GetStringMapString returns the value associated with the key as a map of strings.
|
||||||
func GetStringMapString(key string) map[string]string { return v.GetStringMapString(key) }
|
func 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 {
|
func (v *Viper) GetStringMapString(key string) map[string]string {
|
||||||
return cast.ToStringMapString(v.Get(key))
|
return cast.ToStringMapString(v.Get(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings.
|
// GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings.
|
||||||
func GetStringMapStringSlice(key string) map[string][]string { return v.GetStringMapStringSlice(key) }
|
func 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 {
|
func (v *Viper) GetStringMapStringSlice(key string) map[string][]string {
|
||||||
return cast.ToStringMapStringSlice(v.Get(key))
|
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
|
// GetSizeInBytes returns the size of the value associated with the given key
|
||||||
// in bytes.
|
// in bytes.
|
||||||
func GetSizeInBytes(key string) uint { return v.GetSizeInBytes(key) }
|
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 {
|
func (v *Viper) GetSizeInBytes(key string) uint {
|
||||||
sizeStr := cast.ToString(v.Get(key))
|
sizeStr := cast.ToString(v.Get(key))
|
||||||
return parseSizeInBytes(sizeStr)
|
return parseSizeInBytes(sizeStr)
|
||||||
|
@ -639,6 +727,8 @@ func (v *Viper) GetSizeInBytes(key string) uint {
|
||||||
func UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConfigOption) error {
|
func UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConfigOption) error {
|
||||||
return v.UnmarshalKey(key, rawVal, opts...)
|
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 {
|
func (v *Viper) UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConfigOption) error {
|
||||||
err := decode(v.Get(key), defaultDecoderConfig(rawVal, opts...))
|
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 {
|
func Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error {
|
||||||
return v.Unmarshal(rawVal, opts...)
|
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 {
|
func (v *Viper) Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error {
|
||||||
err := decode(v.AllSettings(), defaultDecoderConfig(rawVal, opts...))
|
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
|
// BindPFlags binds a full flag set to the configuration, using each flag's long
|
||||||
// name as the config key.
|
// name as the config key.
|
||||||
func BindPFlags(flags *pflag.FlagSet) error { return v.BindPFlags(flags) }
|
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 {
|
func (v *Viper) BindPFlags(flags *pflag.FlagSet) error {
|
||||||
return v.BindFlagValues(pflagValueSet{flags})
|
return v.BindFlagValues(pflagValueSet{flags})
|
||||||
}
|
}
|
||||||
|
@ -726,6 +822,13 @@ func (v *Viper) BindPFlags(flags *pflag.FlagSet) error {
|
||||||
// Viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
|
// Viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
|
||||||
//
|
//
|
||||||
func BindPFlag(key string, flag *pflag.Flag) error { return v.BindPFlag(key, flag) }
|
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 {
|
func (v *Viper) BindPFlag(key string, flag *pflag.Flag) error {
|
||||||
return v.BindFlagValue(key, pflagValue{flag})
|
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
|
// BindFlagValues binds a full FlagValue set to the configuration, using each flag's long
|
||||||
// name as the config key.
|
// name as the config key.
|
||||||
func BindFlagValues(flags FlagValueSet) error { return v.BindFlagValues(flags) }
|
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) {
|
func (v *Viper) BindFlagValues(flags FlagValueSet) (err error) {
|
||||||
flags.VisitAll(func(flag FlagValue) {
|
flags.VisitAll(func(flag FlagValue) {
|
||||||
if err = v.BindFlagValue(flag.Name(), flag); err != nil {
|
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"))
|
// Viper.BindFlagValue("port", serverCmd.Flags().Lookup("port"))
|
||||||
//
|
//
|
||||||
func BindFlagValue(key string, flag FlagValue) error { return v.BindFlagValue(key, flag) }
|
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 {
|
func (v *Viper) BindFlagValue(key string, flag FlagValue) error {
|
||||||
if flag == nil {
|
if flag == nil {
|
||||||
return fmt.Errorf("flag for %q is nil", key)
|
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.
|
// 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.
|
// EnvPrefix will be used when set when env name is not provided.
|
||||||
func BindEnv(input ...string) error { return v.BindEnv(input...) }
|
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 {
|
func (v *Viper) BindEnv(input ...string) error {
|
||||||
var key, envkey string
|
var key, envkey string
|
||||||
if len(input) == 0 {
|
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 checks to see if the key has been set in any of the data locations.
|
||||||
// IsSet is case-insensitive for a key.
|
// IsSet is case-insensitive for a key.
|
||||||
func IsSet(key string) bool { return v.IsSet(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 {
|
func (v *Viper) IsSet(key string) bool {
|
||||||
lcaseKey := strings.ToLower(key)
|
lcaseKey := strings.ToLower(key)
|
||||||
val := v.find(lcaseKey)
|
val := v.find(lcaseKey)
|
||||||
|
@ -926,6 +1047,9 @@ func (v *Viper) IsSet(key string) bool {
|
||||||
// AutomaticEnv has Viper check ENV variables for all.
|
// AutomaticEnv has Viper check ENV variables for all.
|
||||||
// keys set in config, default & flags
|
// keys set in config, default & flags
|
||||||
func AutomaticEnv() { v.AutomaticEnv() }
|
func AutomaticEnv() { v.AutomaticEnv() }
|
||||||
|
|
||||||
|
// AutomaticEnv has Viper check ENV variables for all.
|
||||||
|
// keys set in config, default & flags
|
||||||
func (v *Viper) AutomaticEnv() {
|
func (v *Viper) AutomaticEnv() {
|
||||||
v.automaticEnvApplied = true
|
v.automaticEnvApplied = true
|
||||||
}
|
}
|
||||||
|
@ -934,13 +1058,20 @@ func (v *Viper) AutomaticEnv() {
|
||||||
// Useful for mapping an environmental variable to a key that does
|
// Useful for mapping an environmental variable to a key that does
|
||||||
// not match it.
|
// not match it.
|
||||||
func SetEnvKeyReplacer(r *strings.Replacer) { v.SetEnvKeyReplacer(r) }
|
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) {
|
func (v *Viper) SetEnvKeyReplacer(r *strings.Replacer) {
|
||||||
v.envKeyReplacer = r
|
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
|
// This enables one to change a name without breaking the application
|
||||||
func RegisterAlias(alias string, key string) { v.RegisterAlias(alias, key) }
|
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) {
|
func (v *Viper) RegisterAlias(alias string, key string) {
|
||||||
v.registerAlias(alias, strings.ToLower(key))
|
v.registerAlias(alias, strings.ToLower(key))
|
||||||
}
|
}
|
||||||
|
@ -973,14 +1104,14 @@ func (v *Viper) registerAlias(alias string, key string) {
|
||||||
v.aliases[alias] = key
|
v.aliases[alias] = key
|
||||||
}
|
}
|
||||||
} else {
|
} 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 {
|
func (v *Viper) realKey(key string) string {
|
||||||
newkey, exists := v.aliases[key]
|
newkey, exists := v.aliases[key]
|
||||||
if exists {
|
if exists {
|
||||||
jww.DEBUG.Println("Alias", key, "to", newkey)
|
v.logger.Debugf("Alias key %s to: %s", key, newkey)
|
||||||
return v.realKey(newkey)
|
return v.realKey(newkey)
|
||||||
}
|
}
|
||||||
return key
|
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.
|
// InConfig checks to see if the given key (or an alias) is in the config file.
|
||||||
func InConfig(key string) bool { return v.InConfig(key) }
|
func 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 {
|
func (v *Viper) InConfig(key string) bool {
|
||||||
// if the requested key is an alias, then return the proper key
|
// if the requested key is an alias, then return the proper key
|
||||||
key = v.realKey(key)
|
key = v.realKey(key)
|
||||||
|
@ -1000,6 +1133,10 @@ func (v *Viper) InConfig(key string) bool {
|
||||||
// SetDefault is case-insensitive for a key.
|
// SetDefault is case-insensitive for a key.
|
||||||
// Default only used when no value is provided by the user via flag, config or ENV.
|
// Default only used when no value is provided by the user via flag, config or ENV.
|
||||||
func SetDefault(key string, value interface{}) { v.SetDefault(key, value) }
|
func 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{}) {
|
func (v *Viper) SetDefault(key string, value interface{}) {
|
||||||
// If alias passed in, then set the proper default
|
// If alias passed in, then set the proper default
|
||||||
key = v.realKey(strings.ToLower(key))
|
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
|
// Will be used instead of values obtained via
|
||||||
// flags, config file, ENV, default, or key/value store.
|
// flags, config file, ENV, default, or key/value store.
|
||||||
func Set(key string, value interface{}) { v.Set(key, value) }
|
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{}) {
|
func (v *Viper) Set(key string, value interface{}) {
|
||||||
// If alias passed in, then set the proper override
|
// If alias passed in, then set the proper override
|
||||||
key = v.realKey(strings.ToLower(key))
|
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
|
// ReadInConfig will discover and load the configuration file from disk
|
||||||
// and key/value stores, searching in one of the defined paths.
|
// and key/value stores, searching in one of the defined paths.
|
||||||
func ReadInConfig() error { return v.ReadInConfig() }
|
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 {
|
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()
|
filename, err := v.getConfigFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1045,7 +1190,7 @@ func (v *Viper) ReadInConfig() error {
|
||||||
return UnsupportedConfigError(v.getConfigType())
|
return UnsupportedConfigError(v.getConfigType())
|
||||||
}
|
}
|
||||||
|
|
||||||
jww.DEBUG.Println("Reading file: ", filename)
|
v.logger.Debugf("eading file: %s", filename)
|
||||||
file, err := afero.ReadFile(v.fs, filename)
|
file, err := afero.ReadFile(v.fs, filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1064,8 +1209,10 @@ func (v *Viper) ReadInConfig() error {
|
||||||
|
|
||||||
// MergeInConfig merges a new configuration with an existing config.
|
// MergeInConfig merges a new configuration with an existing config.
|
||||||
func MergeInConfig() error { return v.MergeInConfig() }
|
func MergeInConfig() error { return v.MergeInConfig() }
|
||||||
|
|
||||||
|
// MergeInConfig merges a new configuration with an existing config.
|
||||||
func (v *Viper) MergeInConfig() error {
|
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()
|
filename, err := v.getConfigFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1086,6 +1233,9 @@ func (v *Viper) MergeInConfig() error {
|
||||||
// ReadConfig will read a configuration file, setting existing keys to nil if the
|
// ReadConfig will read a configuration file, setting existing keys to nil if the
|
||||||
// key does not exist in the file.
|
// key does not exist in the file.
|
||||||
func ReadConfig(in io.Reader) error { return v.ReadConfig(in) }
|
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 {
|
func (v *Viper) ReadConfig(in io.Reader) error {
|
||||||
v.config = make(map[string]interface{})
|
v.config = make(map[string]interface{})
|
||||||
return v.unmarshalReader(in, v.config)
|
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.
|
// MergeConfig merges a new configuration with an existing config.
|
||||||
func MergeConfig(in io.Reader) error { return v.MergeConfig(in) }
|
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 {
|
func (v *Viper) MergeConfig(in io.Reader) error {
|
||||||
if v.config == nil {
|
if v.config == nil {
|
||||||
v.config = make(map[string]interface{})
|
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.
|
// WriteConfig writes the current configuration to a file.
|
||||||
func WriteConfig() error { return v.WriteConfig() }
|
func WriteConfig() error { return v.WriteConfig() }
|
||||||
|
|
||||||
|
// WriteConfig writes the current configuration to a file.
|
||||||
func (v *Viper) WriteConfig() error {
|
func (v *Viper) WriteConfig() error {
|
||||||
filename, err := v.getConfigFile()
|
filename, err := v.getConfigFile()
|
||||||
if err != nil {
|
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.
|
// SafeWriteConfig writes current configuration to file only if the file does not exist.
|
||||||
func SafeWriteConfig() error { return v.SafeWriteConfig() }
|
func SafeWriteConfig() error { return v.SafeWriteConfig() }
|
||||||
|
|
||||||
|
// SafeWriteConfig writes current configuration to file only if the file does not exist.
|
||||||
func (v *Viper) SafeWriteConfig() error {
|
func (v *Viper) SafeWriteConfig() error {
|
||||||
filename, err := v.getConfigFile()
|
filename, err := v.getConfigFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1127,22 +1283,26 @@ func (v *Viper) SafeWriteConfig() error {
|
||||||
|
|
||||||
// WriteConfigAs writes current configuration to a given filename.
|
// WriteConfigAs writes current configuration to a given filename.
|
||||||
func WriteConfigAs(filename string) error { return v.WriteConfigAs(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 {
|
func (v *Viper) WriteConfigAs(filename string) error {
|
||||||
return v.writeConfig(filename, true)
|
return v.writeConfig(filename, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SafeWriteConfigAs writes current configuration to a given filename if it does not exist.
|
// SafeWriteConfigAs writes current configuration to a given filename if it does not exist.
|
||||||
func SafeWriteConfigAs(filename string) error { return v.SafeWriteConfigAs(filename) }
|
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 {
|
func (v *Viper) SafeWriteConfigAs(filename string) error {
|
||||||
return v.writeConfig(filename, false)
|
return v.writeConfig(filename, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeConfig(filename string, force bool) error { return v.writeConfig(filename, force) }
|
func writeConfig(filename string, force bool) error { return v.writeConfig(filename, force) }
|
||||||
func (v *Viper) writeConfig(filename string, force bool) error {
|
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)
|
ext := filepath.Ext(filename)
|
||||||
if len(ext) <= 1 {
|
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:]
|
configType := ext[1:]
|
||||||
if !stringInSlice(configType, SupportedExts) {
|
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) {
|
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
||||||
flags = os.O_WRONLY
|
flags = os.O_WRONLY
|
||||||
} else {
|
} 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))
|
f, err := v.fs.OpenFile(filename, flags, os.FileMode(0644))
|
||||||
|
@ -1255,7 +1415,7 @@ func mergeMaps(
|
||||||
for sk, sv := range src {
|
for sk, sv := range src {
|
||||||
tk := keyExists(sk, tgt)
|
tk := keyExists(sk, tgt)
|
||||||
if tk == "" {
|
if tk == "" {
|
||||||
jww.TRACE.Printf("tk=\"\", tgt[%s]=%v", sk, sv)
|
v.logger.Debugf("tk=\"\", tgt[%s]=%v", sk, sv)
|
||||||
tgt[sk] = sv
|
tgt[sk] = sv
|
||||||
if itgt != nil {
|
if itgt != nil {
|
||||||
itgt[sk] = sv
|
itgt[sk] = sv
|
||||||
|
@ -1265,7 +1425,7 @@ func mergeMaps(
|
||||||
|
|
||||||
tv, ok := tgt[tk]
|
tv, ok := tgt[tk]
|
||||||
if !ok {
|
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
|
tgt[sk] = sv
|
||||||
if itgt != nil {
|
if itgt != nil {
|
||||||
itgt[sk] = sv
|
itgt[sk] = sv
|
||||||
|
@ -1276,27 +1436,24 @@ func mergeMaps(
|
||||||
svType := reflect.TypeOf(sv)
|
svType := reflect.TypeOf(sv)
|
||||||
tvType := reflect.TypeOf(tv)
|
tvType := reflect.TypeOf(tv)
|
||||||
if svType != tvType {
|
if svType != tvType {
|
||||||
jww.ERROR.Printf(
|
v.logger.Errorf("svType != tvType; key=%s, st=%v, tt=%v, sv=%v, tv=%v", sk, svType, tvType, sv, tv)
|
||||||
"svType != tvType; key=%s, st=%v, tt=%v, sv=%v, tv=%v",
|
|
||||||
sk, svType, tvType, sv, tv)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
jww.TRACE.Printf("processing key=%s, st=%v, tt=%v, sv=%v, tv=%v",
|
v.logger.Debugf("processing key=%s, st=%v, tt=%v, sv=%v, tv=%v", sk, svType, tvType, sv, tv)
|
||||||
sk, svType, tvType, sv, tv)
|
|
||||||
|
|
||||||
switch ttv := tv.(type) {
|
switch ttv := tv.(type) {
|
||||||
case map[interface{}]interface{}:
|
case map[interface{}]interface{}:
|
||||||
jww.TRACE.Printf("merging maps (must convert)")
|
v.logger.Debugf("merging maps (must convert)")
|
||||||
tsv := sv.(map[interface{}]interface{})
|
tsv := sv.(map[interface{}]interface{})
|
||||||
ssv := castToMapStringInterface(tsv)
|
ssv := castToMapStringInterface(tsv)
|
||||||
stv := castToMapStringInterface(ttv)
|
stv := castToMapStringInterface(ttv)
|
||||||
mergeMaps(ssv, stv, ttv)
|
mergeMaps(ssv, stv, ttv)
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
jww.TRACE.Printf("merging maps")
|
v.logger.Debugf("merging maps")
|
||||||
mergeMaps(sv.(map[string]interface{}), ttv, nil)
|
mergeMaps(sv.(map[string]interface{}), ttv, nil)
|
||||||
default:
|
default:
|
||||||
jww.TRACE.Printf("setting value")
|
v.logger.Debugf("setting value")
|
||||||
tgt[tk] = sv
|
tgt[tk] = sv
|
||||||
if itgt != nil {
|
if itgt != nil {
|
||||||
itgt[tk] = sv
|
itgt[tk] = sv
|
||||||
|
@ -1315,6 +1472,9 @@ func (v *Viper) insensitiviseMaps() {
|
||||||
// AllKeys returns all keys holding a value, regardless of where they are set.
|
// AllKeys returns all keys holding a value, regardless of where they are set.
|
||||||
// Nested keys are returned with a v.keyDelim (= ".") separator
|
// Nested keys are returned with a v.keyDelim (= ".") separator
|
||||||
func AllKeys() []string { return v.AllKeys() }
|
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 {
|
func (v *Viper) AllKeys() []string {
|
||||||
m := map[string]bool{}
|
m := map[string]bool{}
|
||||||
// add all paths, by order of descending priority to ensure correct shadowing
|
// 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 {
|
func (v *Viper) mergeFlatMap(shadow map[string]bool, m map[string]interface{}) map[string]bool {
|
||||||
// scan keys
|
// scan keys
|
||||||
outer:
|
outer:
|
||||||
for k, _ := range m {
|
for k := range m {
|
||||||
path := strings.Split(k, v.keyDelim)
|
path := strings.Split(k, v.keyDelim)
|
||||||
// scan intermediate paths
|
// scan intermediate paths
|
||||||
var parentKey string
|
var parentKey string
|
||||||
|
@ -1395,6 +1555,8 @@ outer:
|
||||||
|
|
||||||
// AllSettings merges all settings and returns them as a map[string]interface{}.
|
// AllSettings merges all settings and returns them as a map[string]interface{}.
|
||||||
func AllSettings() map[string]interface{} { return v.AllSettings() }
|
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{} {
|
func (v *Viper) AllSettings() map[string]interface{} {
|
||||||
m := map[string]interface{}{}
|
m := map[string]interface{}{}
|
||||||
// start from the list of keys, and construct the map one value at a time
|
// 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.
|
// SetFs sets the filesystem to use to read configuration.
|
||||||
func SetFs(fs afero.Fs) { v.SetFs(fs) }
|
func SetFs(fs afero.Fs) { v.SetFs(fs) }
|
||||||
|
|
||||||
|
// SetFs sets the filesystem to use to read configuration.
|
||||||
func (v *Viper) SetFs(fs afero.Fs) {
|
func (v *Viper) SetFs(fs afero.Fs) {
|
||||||
v.fs = fs
|
v.fs = fs
|
||||||
}
|
}
|
||||||
|
@ -1423,6 +1587,9 @@ func (v *Viper) SetFs(fs afero.Fs) {
|
||||||
// SetConfigName sets name for the config file.
|
// SetConfigName sets name for the config file.
|
||||||
// Does not include extension.
|
// Does not include extension.
|
||||||
func SetConfigName(in string) { v.SetConfigName(in) }
|
func SetConfigName(in string) { v.SetConfigName(in) }
|
||||||
|
|
||||||
|
// SetConfigName sets name for the config file.
|
||||||
|
// Does not include extension.
|
||||||
func (v *Viper) SetConfigName(in string) {
|
func (v *Viper) SetConfigName(in string) {
|
||||||
if in != "" {
|
if in != "" {
|
||||||
v.configName = 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 {
|
func (v *Viper) getConfigType() string {
|
||||||
if v.configType != "" {
|
|
||||||
return v.configType
|
|
||||||
}
|
|
||||||
|
|
||||||
cf, err := v.getConfigFile()
|
cf, err := v.getConfigFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1470,11 +1625,11 @@ func (v *Viper) getConfigFile() (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Viper) searchInPath(in string) (filename string) {
|
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 {
|
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 {
|
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)
|
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.
|
// Search all configPaths for any config file.
|
||||||
// Returns the first path that exists (and is a config file).
|
// Returns the first path that exists (and is a config file).
|
||||||
func (v *Viper) findConfigFile() (string, error) {
|
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 {
|
for _, cp := range v.configPaths {
|
||||||
file := v.searchInPath(cp)
|
file := v.searchInPath(cp)
|
||||||
|
@ -1499,6 +1654,9 @@ func (v *Viper) findConfigFile() (string, error) {
|
||||||
// Debug prints all configuration registries for debugging
|
// Debug prints all configuration registries for debugging
|
||||||
// purposes.
|
// purposes.
|
||||||
func Debug() { v.Debug() }
|
func Debug() { v.Debug() }
|
||||||
|
|
||||||
|
// Debug prints all configuration registries for debugging
|
||||||
|
// purposes.
|
||||||
func (v *Viper) Debug() {
|
func (v *Viper) Debug() {
|
||||||
fmt.Printf("Aliases:\n%#v\n", v.aliases)
|
fmt.Printf("Aliases:\n%#v\n", v.aliases)
|
||||||
fmt.Printf("Override:\n%#v\n", v.override)
|
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("Config:\n%#v\n", v.config)
|
||||||
fmt.Printf("Defaults:\n%#v\n", v.defaults)
|
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