mirror of
https://github.com/spf13/viper
synced 2024-11-20 03:47:05 +00:00
(#373) Remove key insensitivity
This commit is contained in:
parent
5e97c1a825
commit
a12e6c8300
5 changed files with 73 additions and 181 deletions
|
@ -66,7 +66,7 @@ item below it:
|
||||||
* key/value store
|
* key/value store
|
||||||
* default
|
* default
|
||||||
|
|
||||||
Viper configuration keys are case insensitive.
|
Viper configuration keys are case sensitive.
|
||||||
|
|
||||||
## Putting Values into Viper
|
## Putting Values into Viper
|
||||||
|
|
||||||
|
@ -512,7 +512,7 @@ has been provided.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```go
|
```go
|
||||||
viper.GetString("logfile") // case-insensitive Setting & Getting
|
viper.GetString("logfile") // case-sensitive Setting & Getting
|
||||||
if viper.GetBool("verbose") {
|
if viper.GetBool("verbose") {
|
||||||
fmt.Println("verbose enabled")
|
fmt.Println("verbose enabled")
|
||||||
}
|
}
|
||||||
|
|
55
util.go
55
util.go
|
@ -33,61 +33,6 @@ func (pe ConfigParseError) Error() string {
|
||||||
return fmt.Sprintf("While parsing config: %s", pe.err.Error())
|
return fmt.Sprintf("While parsing config: %s", pe.err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// toCaseInsensitiveValue checks if the value is a map;
|
|
||||||
// if so, create a copy and lower-case the keys recursively.
|
|
||||||
func toCaseInsensitiveValue(value interface{}) interface{} {
|
|
||||||
switch v := value.(type) {
|
|
||||||
case map[interface{}]interface{}:
|
|
||||||
value = copyAndInsensitiviseMap(cast.ToStringMap(v))
|
|
||||||
case map[string]interface{}:
|
|
||||||
value = copyAndInsensitiviseMap(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
// copyAndInsensitiviseMap behaves like insensitiviseMap, but creates a copy of
|
|
||||||
// any map it makes case insensitive.
|
|
||||||
func copyAndInsensitiviseMap(m map[string]interface{}) map[string]interface{} {
|
|
||||||
nm := make(map[string]interface{})
|
|
||||||
|
|
||||||
for key, val := range m {
|
|
||||||
lkey := strings.ToLower(key)
|
|
||||||
switch v := val.(type) {
|
|
||||||
case map[interface{}]interface{}:
|
|
||||||
nm[lkey] = copyAndInsensitiviseMap(cast.ToStringMap(v))
|
|
||||||
case map[string]interface{}:
|
|
||||||
nm[lkey] = copyAndInsensitiviseMap(v)
|
|
||||||
default:
|
|
||||||
nm[lkey] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nm
|
|
||||||
}
|
|
||||||
|
|
||||||
func insensitiviseMap(m map[string]interface{}) {
|
|
||||||
for key, val := range m {
|
|
||||||
switch val.(type) {
|
|
||||||
case map[interface{}]interface{}:
|
|
||||||
// nested map: cast and recursively insensitivise
|
|
||||||
val = cast.ToStringMap(val)
|
|
||||||
insensitiviseMap(val.(map[string]interface{}))
|
|
||||||
case map[string]interface{}:
|
|
||||||
// nested map: recursively insensitivise
|
|
||||||
insensitiviseMap(val.(map[string]interface{}))
|
|
||||||
}
|
|
||||||
|
|
||||||
lower := strings.ToLower(key)
|
|
||||||
if key != lower {
|
|
||||||
// remove old key (not lower-cased)
|
|
||||||
delete(m, key)
|
|
||||||
}
|
|
||||||
// update map
|
|
||||||
m[lower] = val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func absPathify(inPath string) string {
|
func absPathify(inPath string) string {
|
||||||
jww.INFO.Println("Trying to resolve absolute path to", inPath)
|
jww.INFO.Println("Trying to resolve absolute path to", inPath)
|
||||||
|
|
||||||
|
|
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")
|
|
||||||
}
|
|
||||||
}
|
|
53
viper.go
53
viper.go
|
@ -493,7 +493,7 @@ func (v *Viper) providerPathExists(p *defaultRemoteProvider) bool {
|
||||||
|
|
||||||
// searchMap recursively searches for a value for path in source map.
|
// searchMap recursively searches for a value for path in source map.
|
||||||
// Returns nil if not found.
|
// Returns nil if not found.
|
||||||
// Note: This assumes that the path entries and map keys are lower cased.
|
// Note: This assumes that the path entries and map keys are cased sensitive.
|
||||||
func (v *Viper) searchMap(source map[string]interface{}, path []string) interface{} {
|
func (v *Viper) searchMap(source map[string]interface{}, path []string) interface{} {
|
||||||
if len(path) == 0 {
|
if len(path) == 0 {
|
||||||
return source
|
return source
|
||||||
|
@ -532,7 +532,7 @@ func (v *Viper) searchMap(source map[string]interface{}, path []string) interfac
|
||||||
// This should be useful only at config level (other maps may not contain dots
|
// This should be useful only at config level (other maps may not contain dots
|
||||||
// in their keys).
|
// in their keys).
|
||||||
//
|
//
|
||||||
// Note: This assumes that the path entries and map keys are lower cased.
|
// Note: This assumes that the path entries and map keys are case sensitive.
|
||||||
func (v *Viper) searchMapWithPathPrefixes(source map[string]interface{}, path []string) interface{} {
|
func (v *Viper) searchMapWithPathPrefixes(source map[string]interface{}, path []string) interface{} {
|
||||||
if len(path) == 0 {
|
if len(path) == 0 {
|
||||||
return source
|
return source
|
||||||
|
@ -540,7 +540,7 @@ func (v *Viper) searchMapWithPathPrefixes(source map[string]interface{}, path []
|
||||||
|
|
||||||
// search for path prefixes, starting from the longest one
|
// search for path prefixes, starting from the longest one
|
||||||
for i := len(path); i > 0; i-- {
|
for i := len(path); i > 0; i-- {
|
||||||
prefixKey := strings.ToLower(strings.Join(path[0:i], v.keyDelim))
|
prefixKey := strings.Join(path[0:i], v.keyDelim)
|
||||||
|
|
||||||
next, ok := source[prefixKey]
|
next, ok := source[prefixKey]
|
||||||
if ok {
|
if ok {
|
||||||
|
@ -661,7 +661,7 @@ func GetViper() *Viper {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get can retrieve any value given the key to use.
|
// Get can retrieve any value given the key to use.
|
||||||
// Get is case-insensitive for a key.
|
// Get is case-sensitive for a key.
|
||||||
// Get has the behavior of returning the value associated with the first
|
// 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:
|
// place from where it is set. Viper will check in the following order:
|
||||||
// override, flag, env, config file, key/value store, default
|
// override, flag, env, config file, key/value store, default
|
||||||
|
@ -669,7 +669,7 @@ 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) }
|
||||||
func (v *Viper) Get(key string) interface{} {
|
func (v *Viper) Get(key string) interface{} {
|
||||||
lcaseKey := strings.ToLower(key)
|
lcaseKey := key
|
||||||
val := v.find(lcaseKey)
|
val := v.find(lcaseKey)
|
||||||
if val == nil {
|
if val == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -716,7 +716,7 @@ 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-sensitive for a key.
|
||||||
func Sub(key string) *Viper { return v.Sub(key) }
|
func Sub(key string) *Viper { return v.Sub(key) }
|
||||||
func (v *Viper) Sub(key string) *Viper {
|
func (v *Viper) Sub(key string) *Viper {
|
||||||
subv := New()
|
subv := New()
|
||||||
|
@ -948,7 +948,7 @@ 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)
|
||||||
}
|
}
|
||||||
v.pflags[strings.ToLower(key)] = flag
|
v.pflags[key] = flag
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -963,7 +963,7 @@ func (v *Viper) BindEnv(input ...string) error {
|
||||||
return fmt.Errorf("BindEnv missing key to bind to")
|
return fmt.Errorf("BindEnv missing key to bind to")
|
||||||
}
|
}
|
||||||
|
|
||||||
key = strings.ToLower(input[0])
|
key = input[0]
|
||||||
|
|
||||||
if len(input) == 1 {
|
if len(input) == 1 {
|
||||||
envkey = v.mergeWithEnvPrefix(key)
|
envkey = v.mergeWithEnvPrefix(key)
|
||||||
|
@ -980,7 +980,7 @@ func (v *Viper) BindEnv(input ...string) error {
|
||||||
// Viper will check in the following order:
|
// Viper will check in the following order:
|
||||||
// flag, env, config file, key/value store, default.
|
// flag, env, config file, key/value store, default.
|
||||||
// Viper will check to see if an alias exists first.
|
// Viper will check to see if an alias exists first.
|
||||||
// Note: this assumes a lower-cased key given.
|
// Note: this assumes a case-sensitive key given.
|
||||||
func (v *Viper) find(lcaseKey string) interface{} {
|
func (v *Viper) find(lcaseKey string) interface{} {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -1120,10 +1120,10 @@ 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-sensitive for a key.
|
||||||
func IsSet(key string) bool { return v.IsSet(key) }
|
func IsSet(key string) bool { return v.IsSet(key) }
|
||||||
func (v *Viper) IsSet(key string) bool {
|
func (v *Viper) IsSet(key string) bool {
|
||||||
lcaseKey := strings.ToLower(key)
|
lcaseKey := key
|
||||||
val := v.find(lcaseKey)
|
val := v.find(lcaseKey)
|
||||||
return val != nil
|
return val != nil
|
||||||
}
|
}
|
||||||
|
@ -1147,11 +1147,10 @@ func (v *Viper) SetEnvKeyReplacer(r *strings.Replacer) {
|
||||||
// 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) }
|
||||||
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, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Viper) registerAlias(alias string, key string) {
|
func (v *Viper) registerAlias(alias string, key string) {
|
||||||
alias = strings.ToLower(alias)
|
|
||||||
if alias != key && alias != v.realKey(key) {
|
if alias != key && alias != v.realKey(key) {
|
||||||
_, exists := v.aliases[alias]
|
_, exists := v.aliases[alias]
|
||||||
|
|
||||||
|
@ -1202,16 +1201,15 @@ func (v *Viper) InConfig(key string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDefault sets the default value for this key.
|
// SetDefault sets the default value for this key.
|
||||||
// SetDefault is case-insensitive for a key.
|
// SetDefault is case-sensitive 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) }
|
||||||
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(key)
|
||||||
value = toCaseInsensitiveValue(value)
|
|
||||||
|
|
||||||
path := strings.Split(key, v.keyDelim)
|
path := strings.Split(key, v.keyDelim)
|
||||||
lastKey := strings.ToLower(path[len(path)-1])
|
lastKey := path[len(path)-1]
|
||||||
deepestMap := deepSearch(v.defaults, path[0:len(path)-1])
|
deepestMap := deepSearch(v.defaults, path[0:len(path)-1])
|
||||||
|
|
||||||
// set innermost value
|
// set innermost value
|
||||||
|
@ -1219,17 +1217,16 @@ func (v *Viper) SetDefault(key string, value interface{}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set sets the value for the key in the override register.
|
// Set sets the value for the key in the override register.
|
||||||
// Set is case-insensitive for a key.
|
// Set is case-sensitive for a key.
|
||||||
// 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) }
|
||||||
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(key)
|
||||||
value = toCaseInsensitiveValue(value)
|
|
||||||
|
|
||||||
path := strings.Split(key, v.keyDelim)
|
path := strings.Split(key, v.keyDelim)
|
||||||
lastKey := strings.ToLower(path[len(path)-1])
|
lastKey := path[len(path)-1]
|
||||||
deepestMap := deepSearch(v.override, path[0:len(path)-1])
|
deepestMap := deepSearch(v.override, path[0:len(path)-1])
|
||||||
|
|
||||||
// set innermost value
|
// set innermost value
|
||||||
|
@ -1313,7 +1310,6 @@ func (v *Viper) MergeConfigMap(cfg map[string]interface{}) error {
|
||||||
if v.config == nil {
|
if v.config == nil {
|
||||||
v.config = make(map[string]interface{})
|
v.config = make(map[string]interface{})
|
||||||
}
|
}
|
||||||
insensitiviseMap(cfg)
|
|
||||||
mergeMaps(cfg, v.config, nil)
|
mergeMaps(cfg, v.config, nil)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1439,14 +1435,13 @@ func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error {
|
||||||
value, _ := v.properties.Get(key)
|
value, _ := v.properties.Get(key)
|
||||||
// recursively build nested maps
|
// recursively build nested maps
|
||||||
path := strings.Split(key, ".")
|
path := strings.Split(key, ".")
|
||||||
lastKey := strings.ToLower(path[len(path)-1])
|
lastKey := path[len(path)-1]
|
||||||
deepestMap := deepSearch(c, path[0:len(path)-1])
|
deepestMap := deepSearch(c, path[0:len(path)-1])
|
||||||
// set innermost value
|
// set innermost value
|
||||||
deepestMap[lastKey] = value
|
deepestMap[lastKey] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
insensitiviseMap(c)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1532,9 +1527,9 @@ func (v *Viper) marshalWriter(f afero.File, configType string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func keyExists(k string, m map[string]interface{}) string {
|
func keyExists(k string, m map[string]interface{}) string {
|
||||||
lk := strings.ToLower(k)
|
lk := k
|
||||||
for mk := range m {
|
for mk := range m {
|
||||||
lmk := strings.ToLower(mk)
|
lmk := mk
|
||||||
if lmk == lk {
|
if lmk == lk {
|
||||||
return mk
|
return mk
|
||||||
}
|
}
|
||||||
|
@ -1758,7 +1753,7 @@ func (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[string]interfac
|
||||||
m2 = cast.ToStringMap(val)
|
m2 = cast.ToStringMap(val)
|
||||||
default:
|
default:
|
||||||
// immediate value
|
// immediate value
|
||||||
shadow[strings.ToLower(fullKey)] = true
|
shadow[fullKey] = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// recursively merge to shadow map
|
// recursively merge to shadow map
|
||||||
|
@ -1784,7 +1779,7 @@ outer:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// add key
|
// add key
|
||||||
shadow[strings.ToLower(k)] = true
|
shadow[k] = true
|
||||||
}
|
}
|
||||||
return shadow
|
return shadow
|
||||||
}
|
}
|
||||||
|
@ -1802,7 +1797,7 @@ func (v *Viper) AllSettings() map[string]interface{} {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
path := strings.Split(k, v.keyDelim)
|
path := strings.Split(k, v.keyDelim)
|
||||||
lastKey := strings.ToLower(path[len(path)-1])
|
lastKey := path[len(path)-1]
|
||||||
deepestMap := deepSearch(m, path[0:len(path)-1])
|
deepestMap := deepSearch(m, path[0:len(path)-1])
|
||||||
// set innermost value
|
// set innermost value
|
||||||
deepestMap[lastKey] = value
|
deepestMap[lastKey] = value
|
||||||
|
|
|
@ -66,7 +66,7 @@ dob = 1979-05-27T07:32:00Z # First class dates? Why not?`)
|
||||||
var dotenvExample = []byte(`
|
var dotenvExample = []byte(`
|
||||||
TITLE_DOTENV="DotEnv Example"
|
TITLE_DOTENV="DotEnv Example"
|
||||||
TYPE_DOTENV=donut
|
TYPE_DOTENV=donut
|
||||||
NAME_DOTENV=Cake`)
|
name_dotenv=Cake`)
|
||||||
|
|
||||||
var jsonExample = []byte(`{
|
var jsonExample = []byte(`{
|
||||||
"id": "0001",
|
"id": "0001",
|
||||||
|
@ -295,7 +295,7 @@ func TestUnmarshaling(t *testing.T) {
|
||||||
assert.False(t, InConfig("state"))
|
assert.False(t, InConfig("state"))
|
||||||
assert.Equal(t, "steve", Get("name"))
|
assert.Equal(t, "steve", Get("name"))
|
||||||
assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, Get("hobbies"))
|
assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, Get("hobbies"))
|
||||||
assert.Equal(t, map[string]interface{}{"jacket": "leather", "TROUSERS": "denim", "pants": map[string]interface{}{"size": "large"}}, Get("clothing"))
|
assert.Equal(t, map[interface{}]interface{}{"TROUSERS": "denim", "jacket": "leather", "pants": map[interface{}]interface{}{"size": "large"}}, Get("clothing"))
|
||||||
assert.Equal(t, 35, Get("age"))
|
assert.Equal(t, 35, Get("age"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,7 +360,7 @@ func TestTOML(t *testing.T) {
|
||||||
|
|
||||||
func TestDotEnv(t *testing.T) {
|
func TestDotEnv(t *testing.T) {
|
||||||
initDotEnv()
|
initDotEnv()
|
||||||
assert.Equal(t, "DotEnv Example", Get("title_dotenv"))
|
assert.Equal(t, "DotEnv Example", Get("TITLE_DOTENV"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHCL(t *testing.T) {
|
func TestHCL(t *testing.T) {
|
||||||
|
@ -491,11 +491,11 @@ func TestSetEnvKeyReplacer(t *testing.T) {
|
||||||
func TestAllKeys(t *testing.T) {
|
func TestAllKeys(t *testing.T) {
|
||||||
initConfigs()
|
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",
|
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",
|
||||||
"title_dotenv", "type_dotenv", "name_dotenv",
|
"TITLE_DOTENV", "TYPE_DOTENV", "name_dotenv",
|
||||||
}
|
}
|
||||||
dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
|
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}}}}, "title_dotenv": "DotEnv Example", "type_dotenv": "donut", "name_dotenv": "Cake"}
|
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}}}}, "TITLE_DOTENV": "DotEnv Example", "TYPE_DOTENV": "donut", "name_dotenv": "Cake"}
|
||||||
|
|
||||||
allkeys := sort.StringSlice(AllKeys())
|
allkeys := sort.StringSlice(AllKeys())
|
||||||
allkeys.Sort()
|
allkeys.Sort()
|
||||||
|
@ -526,7 +526,7 @@ func TestAliasesOfAliases(t *testing.T) {
|
||||||
Set("Title", "Checking Case")
|
Set("Title", "Checking Case")
|
||||||
RegisterAlias("Foo", "Bar")
|
RegisterAlias("Foo", "Bar")
|
||||||
RegisterAlias("Bar", "Title")
|
RegisterAlias("Bar", "Title")
|
||||||
assert.Equal(t, "Checking Case", Get("FOO"))
|
assert.Equal(t, "Checking Case", Get("Foo"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRecursiveAliases(t *testing.T) {
|
func TestRecursiveAliases(t *testing.T) {
|
||||||
|
@ -773,7 +773,8 @@ func TestBoundCaseSensitivity(t *testing.T) {
|
||||||
BindEnv("eYEs", "TURTLE_EYES")
|
BindEnv("eYEs", "TURTLE_EYES")
|
||||||
os.Setenv("TURTLE_EYES", "blue")
|
os.Setenv("TURTLE_EYES", "blue")
|
||||||
|
|
||||||
assert.Equal(t, "blue", Get("eyes"))
|
assert.Equal(t, "blue", Get("eYEs"))
|
||||||
|
assert.Nil(t, Get("eyeS"))
|
||||||
|
|
||||||
var testString = "green"
|
var testString = "green"
|
||||||
var testValue = newStringValue(testString, &testString)
|
var testValue = newStringValue(testString, &testString)
|
||||||
|
@ -785,7 +786,8 @@ func TestBoundCaseSensitivity(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
BindPFlag("eYEs", flag)
|
BindPFlag("eYEs", flag)
|
||||||
assert.Equal(t, "green", Get("eyes"))
|
assert.Equal(t, "green", Get("eYEs"))
|
||||||
|
assert.Nil(t, Get("Eyes"))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -846,7 +848,7 @@ func TestFindsNestedKeys(t *testing.T) {
|
||||||
},
|
},
|
||||||
"TITLE_DOTENV": "DotEnv Example",
|
"TITLE_DOTENV": "DotEnv Example",
|
||||||
"TYPE_DOTENV": "donut",
|
"TYPE_DOTENV": "donut",
|
||||||
"NAME_DOTENV": "Cake",
|
"name_dotenv": "Cake",
|
||||||
"title": "TOML Example",
|
"title": "TOML Example",
|
||||||
"newkey": "remote",
|
"newkey": "remote",
|
||||||
"batters": map[string]interface{}{
|
"batters": map[string]interface{}{
|
||||||
|
@ -867,19 +869,19 @@ func TestFindsNestedKeys(t *testing.T) {
|
||||||
"age": 35,
|
"age": 35,
|
||||||
"owner": map[string]interface{}{
|
"owner": map[string]interface{}{
|
||||||
"organization": "MongoDB",
|
"organization": "MongoDB",
|
||||||
"bio": "MongoDB Chief Developer Advocate & Hacker at Large",
|
"Bio": "MongoDB Chief Developer Advocate & Hacker at Large",
|
||||||
"dob": dob,
|
"dob": dob,
|
||||||
},
|
},
|
||||||
"owner.bio": "MongoDB Chief Developer Advocate & Hacker at Large",
|
"owner.Bio": "MongoDB Chief Developer Advocate & Hacker at Large",
|
||||||
"type": "donut",
|
"type": "donut",
|
||||||
"id": "0001",
|
"id": "0001",
|
||||||
"name": "Cake",
|
"name": "Cake",
|
||||||
"hacker": true,
|
"Hacker": true,
|
||||||
"ppu": 0.55,
|
"ppu": 0.55,
|
||||||
"clothing": map[string]interface{}{
|
"clothing": map[interface{}]interface{}{
|
||||||
"jacket": "leather",
|
"jacket": "leather",
|
||||||
"TROUSERS": "denim",
|
"TROUSERS": "denim",
|
||||||
"pants": map[string]interface{}{
|
"pants": map[interface{}]interface{}{
|
||||||
"size": "large",
|
"size": "large",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -925,7 +927,7 @@ func TestReadBufConfig(t *testing.T) {
|
||||||
assert.False(t, v.InConfig("state"))
|
assert.False(t, v.InConfig("state"))
|
||||||
assert.Equal(t, "steve", v.Get("name"))
|
assert.Equal(t, "steve", v.Get("name"))
|
||||||
assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, v.Get("hobbies"))
|
assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, v.Get("hobbies"))
|
||||||
assert.Equal(t, map[string]interface{}{"jacket": "leather", "TROUSERS": "denim", "pants": map[string]interface{}{"size": "large"}}, v.Get("clothing"))
|
assert.Equal(t, map[interface{}]interface{}{"TROUSERS": "denim", "jacket": "leather", "pants": map[interface{}]interface{}{"size": "large"}}, v.Get("clothing"))
|
||||||
assert.Equal(t, 35, v.Get("age"))
|
assert.Equal(t, 35, v.Get("age"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1164,7 +1166,7 @@ func TestWriteConfigTOML(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, v.GetString("title"), v2.GetString("title"))
|
assert.Equal(t, v.GetString("title"), v2.GetString("title"))
|
||||||
assert.Equal(t, v.GetString("owner.bio"), v2.GetString("owner.bio"))
|
assert.Equal(t, v.GetString("owner.Bio"), v2.GetString("owner.Bio"))
|
||||||
assert.Equal(t, v.GetString("owner.dob"), v2.GetString("owner.dob"))
|
assert.Equal(t, v.GetString("owner.dob"), v2.GetString("owner.dob"))
|
||||||
assert.Equal(t, v.GetString("owner.organization"), v2.GetString("owner.organization"))
|
assert.Equal(t, v.GetString("owner.organization"), v2.GetString("owner.organization"))
|
||||||
}
|
}
|
||||||
|
@ -1206,15 +1208,15 @@ func TestWriteConfigDotEnv(t *testing.T) {
|
||||||
assert.Equal(t, v.GetString("kind"), v2.GetString("kind"))
|
assert.Equal(t, v.GetString("kind"), v2.GetString("kind"))
|
||||||
}
|
}
|
||||||
|
|
||||||
var yamlWriteExpected = []byte(`age: 35
|
var yamlWriteExpected = []byte(`Hacker: true
|
||||||
|
age: 35
|
||||||
beard: true
|
beard: true
|
||||||
clothing:
|
clothing:
|
||||||
|
TROUSERS: denim
|
||||||
jacket: leather
|
jacket: leather
|
||||||
pants:
|
pants:
|
||||||
size: large
|
size: large
|
||||||
TROUSERS: denim
|
|
||||||
eyes: brown
|
eyes: brown
|
||||||
hacker: true
|
|
||||||
hobbies:
|
hobbies:
|
||||||
- skateboarding
|
- skateboarding
|
||||||
- snowboarding
|
- snowboarding
|
||||||
|
@ -1225,6 +1227,9 @@ name: steve
|
||||||
func TestWriteConfigYAML(t *testing.T) {
|
func TestWriteConfigYAML(t *testing.T) {
|
||||||
v := New()
|
v := New()
|
||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
|
a := afero.Afero{
|
||||||
|
Fs: fs,
|
||||||
|
}
|
||||||
v.SetFs(fs)
|
v.SetFs(fs)
|
||||||
v.SetConfigName("c")
|
v.SetConfigName("c")
|
||||||
v.SetConfigType("yaml")
|
v.SetConfigType("yaml")
|
||||||
|
@ -1235,7 +1240,7 @@ func TestWriteConfigYAML(t *testing.T) {
|
||||||
if err := v.WriteConfigAs("c.yaml"); err != nil {
|
if err := v.WriteConfigAs("c.yaml"); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
read, err := afero.ReadFile(fs, "c.yaml")
|
read, err := a.ReadFile("c.yaml")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -1405,8 +1410,8 @@ func TestMergeConfigMap(t *testing.T) {
|
||||||
assert(37890)
|
assert(37890)
|
||||||
|
|
||||||
update := map[string]interface{}{
|
update := map[string]interface{}{
|
||||||
"Hello": map[string]interface{}{
|
"hello": map[interface{}]interface{}{
|
||||||
"Pop": 1234,
|
"pop": 1234,
|
||||||
},
|
},
|
||||||
"World": map[interface{}]interface{}{
|
"World": map[interface{}]interface{}{
|
||||||
"Rock": 345,
|
"Rock": 345,
|
||||||
|
@ -1417,7 +1422,7 @@ func TestMergeConfigMap(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if rock := v.GetInt("world.rock"); rock != 345 {
|
if rock := v.GetInt("World.Rock"); rock != 345 {
|
||||||
t.Fatal("Got rock:", rock)
|
t.Fatal("Got rock:", rock)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1497,7 +1502,7 @@ func TestDotParameter(t *testing.T) {
|
||||||
assert.Equal(t, expected, actual)
|
assert.Equal(t, expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCaseInsensitive(t *testing.T) {
|
func TestCaseSensitive(t *testing.T) {
|
||||||
for _, config := range []struct {
|
for _, config := range []struct {
|
||||||
typ string
|
typ string
|
||||||
content string
|
content string
|
||||||
|
@ -1538,7 +1543,7 @@ Q = 5
|
||||||
R = 6
|
R = 6
|
||||||
`},
|
`},
|
||||||
} {
|
} {
|
||||||
doTestCaseInsensitive(t, config.typ, config.content)
|
doTestCaseSensitive(t, config.typ, config.content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1567,15 +1572,15 @@ func TestCaseInsensitiveSet(t *testing.T) {
|
||||||
SetDefault("Number2", 52)
|
SetDefault("Number2", 52)
|
||||||
|
|
||||||
// Verify SetDefault
|
// Verify SetDefault
|
||||||
if v := Get("number2"); v != 52 {
|
if v := Get("Number2"); v != 52 {
|
||||||
t.Fatalf("Expected 52 got %q", v)
|
t.Fatalf("Expected 52 got %q", v)
|
||||||
}
|
}
|
||||||
|
|
||||||
if v := Get("given2.foo"); v != 52 {
|
if v := Get("Given2.Foo"); v != 52 {
|
||||||
t.Fatalf("Expected 52 got %q", v)
|
t.Fatalf("Expected 52 got %q", v)
|
||||||
}
|
}
|
||||||
|
|
||||||
if v := Get("given2.bar.bcd"); v != "A" {
|
if v := Get("Given2.Bar.bCd"); v != "A" {
|
||||||
t.Fatalf("Expected A got %q", v)
|
t.Fatalf("Expected A got %q", v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1584,15 +1589,15 @@ func TestCaseInsensitiveSet(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify Set
|
// Verify Set
|
||||||
if v := Get("number1"); v != 42 {
|
if v := Get("Number1"); v != 42 {
|
||||||
t.Fatalf("Expected 42 got %q", v)
|
t.Fatalf("Expected 42 got %q", v)
|
||||||
}
|
}
|
||||||
|
|
||||||
if v := Get("given1.foo"); v != 32 {
|
if v := Get("Given1.Foo"); v != 32 {
|
||||||
t.Fatalf("Expected 32 got %q", v)
|
t.Fatalf("Expected 32 got %q", v)
|
||||||
}
|
}
|
||||||
|
|
||||||
if v := Get("given1.bar.abc"); v != "A" {
|
if v := Get("Given1.Bar.ABc"); v != "A" {
|
||||||
t.Fatalf("Expected A got %q", v)
|
t.Fatalf("Expected A got %q", v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1630,17 +1635,18 @@ func TestParseNested(t *testing.T) {
|
||||||
assert.Equal(t, 200*time.Millisecond, items[0].Nested.Delay)
|
assert.Equal(t, 200*time.Millisecond, items[0].Nested.Delay)
|
||||||
}
|
}
|
||||||
|
|
||||||
func doTestCaseInsensitive(t *testing.T, typ, config string) {
|
func doTestCaseSensitive(t *testing.T, typ, config string) {
|
||||||
initConfig(typ, config)
|
initConfig(typ, config)
|
||||||
Set("RfD", true)
|
Set("RfD", true)
|
||||||
assert.Equal(t, true, Get("rfd"))
|
assert.Nil(t, Get("rfd"))
|
||||||
assert.Equal(t, true, Get("rFD"))
|
assert.Nil(t, Get("rFD"))
|
||||||
assert.Equal(t, 1, cast.ToInt(Get("abcd")))
|
assert.Nil(t, Get("RFD"))
|
||||||
assert.Equal(t, 1, cast.ToInt(Get("Abcd")))
|
assert.Equal(t, true, Get("RfD"))
|
||||||
assert.Equal(t, 2, cast.ToInt(Get("ef.gh")))
|
assert.Equal(t, 1, cast.ToInt(Get("aBcD")))
|
||||||
assert.Equal(t, 3, cast.ToInt(Get("ef.ijk")))
|
assert.Equal(t, 2, cast.ToInt(Get("eF.gH")))
|
||||||
assert.Equal(t, 4, cast.ToInt(Get("ef.lm.no")))
|
assert.Equal(t, 3, cast.ToInt(Get("eF.iJk")))
|
||||||
assert.Equal(t, 5, cast.ToInt(Get("ef.lm.p.q")))
|
assert.Equal(t, 4, cast.ToInt(Get("eF.Lm.nO")))
|
||||||
|
assert.Equal(t, 5, cast.ToInt(Get("eF.Lm.P.Q")))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue