mirror of
https://github.com/spf13/viper
synced 2024-12-23 03:57:01 +00:00
[FEATURE] it's option to set case sensitive
This commit is contained in:
parent
adc3a873f0
commit
856e87ed6d
8 changed files with 77 additions and 33 deletions
|
@ -862,7 +862,16 @@ application foundation needs.
|
||||||
Is there a better name for a [commander](http://en.wikipedia.org/wiki/Cobra_Commander)?
|
Is there a better name for a [commander](http://en.wikipedia.org/wiki/Cobra_Commander)?
|
||||||
|
|
||||||
### Does Viper support case sensitive keys?
|
### Does Viper support case sensitive keys?
|
||||||
|
#### [FEATURE] surport case sensitive
|
||||||
|
```go
|
||||||
|
// if you want to keep case insensitive, you can do nothing
|
||||||
|
// but if you want to make it case sensitive, please do the following step
|
||||||
|
func main(){
|
||||||
|
viper.SetCaseSensitive()
|
||||||
|
|
||||||
|
// your code next...
|
||||||
|
}
|
||||||
|
```
|
||||||
**tl;dr:** No.
|
**tl;dr:** No.
|
||||||
|
|
||||||
Viper merges configuration from various sources, many of which are either case insensitive or uses different casing than the rest of the sources (eg. env vars).
|
Viper merges configuration from various sources, many of which are either case insensitive or uses different casing than the rest of the sources (eg. env vars).
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
package dotenv
|
package dotenv
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
|
insensitiveopt "github.com/spf13/viper/internal/insensitiveOpt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// flattenAndMergeMap recursively flattens the given map into a new map
|
// flattenAndMergeMap recursively flattens the given map into a new map
|
||||||
|
@ -31,7 +30,7 @@ func flattenAndMergeMap(shadow map[string]interface{}, m map[string]interface{},
|
||||||
m2 = cast.ToStringMap(val)
|
m2 = cast.ToStringMap(val)
|
||||||
default:
|
default:
|
||||||
// immediate value
|
// immediate value
|
||||||
shadow[strings.ToLower(fullKey)] = val
|
shadow[insensitiveopt.ToLower(fullKey)] = val
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// recursively merge to shadow map
|
// recursively merge to shadow map
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
package ini
|
package ini
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
|
insensitiveopt "github.com/spf13/viper/internal/insensitiveOpt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// THIS CODE IS COPIED HERE: IT SHOULD NOT BE MODIFIED
|
// THIS CODE IS COPIED HERE: IT SHOULD NOT BE MODIFIED
|
||||||
|
@ -64,7 +63,7 @@ func flattenAndMergeMap(shadow map[string]interface{}, m map[string]interface{},
|
||||||
m2 = cast.ToStringMap(val)
|
m2 = cast.ToStringMap(val)
|
||||||
default:
|
default:
|
||||||
// immediate value
|
// immediate value
|
||||||
shadow[strings.ToLower(fullKey)] = val
|
shadow[insensitiveopt.ToLower(fullKey)] = val
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// recursively merge to shadow map
|
// recursively merge to shadow map
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/magiconair/properties"
|
"github.com/magiconair/properties"
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
|
insensitiveopt "github.com/spf13/viper/internal/insensitiveOpt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for Java properties encoding.
|
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for Java properties encoding.
|
||||||
|
@ -67,7 +68,7 @@ func (c *Codec) Decode(b []byte, v map[string]interface{}) error {
|
||||||
|
|
||||||
// recursively build nested maps
|
// recursively build nested maps
|
||||||
path := strings.Split(key, c.keyDelimiter())
|
path := strings.Split(key, c.keyDelimiter())
|
||||||
lastKey := strings.ToLower(path[len(path)-1])
|
lastKey := insensitiveopt.ToLower(path[len(path)-1])
|
||||||
deepestMap := deepSearch(v, path[0:len(path)-1])
|
deepestMap := deepSearch(v, path[0:len(path)-1])
|
||||||
|
|
||||||
// set innermost value
|
// set innermost value
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
package javaproperties
|
package javaproperties
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
|
insensitiveopt "github.com/spf13/viper/internal/insensitiveOpt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// THIS CODE IS COPIED HERE: IT SHOULD NOT BE MODIFIED
|
// THIS CODE IS COPIED HERE: IT SHOULD NOT BE MODIFIED
|
||||||
|
@ -64,7 +63,7 @@ func flattenAndMergeMap(shadow map[string]interface{}, m map[string]interface{},
|
||||||
m2 = cast.ToStringMap(val)
|
m2 = cast.ToStringMap(val)
|
||||||
default:
|
default:
|
||||||
// immediate value
|
// immediate value
|
||||||
shadow[strings.ToLower(fullKey)] = val
|
shadow[insensitiveopt.ToLower(fullKey)] = val
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// recursively merge to shadow map
|
// recursively merge to shadow map
|
||||||
|
|
28
internal/insensitiveOpt/fix.go
Normal file
28
internal/insensitiveOpt/fix.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package insensitiveopt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
var insensitive = true
|
||||||
|
|
||||||
|
func Insensitive(f bool) {
|
||||||
|
insensitive = f
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToLower(s string) string {
|
||||||
|
if insensitive {
|
||||||
|
return strings.ToLower(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToLowerRune(s rune) rune {
|
||||||
|
if insensitive {
|
||||||
|
return unicode.ToLower(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
8
util.go
8
util.go
|
@ -16,9 +16,9 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
|
||||||
|
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
|
insensitiveopt "github.com/spf13/viper/internal/insensitiveOpt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConfigParseError denotes failing to parse configuration file.
|
// ConfigParseError denotes failing to parse configuration file.
|
||||||
|
@ -50,7 +50,7 @@ func copyAndInsensitiviseMap(m map[string]interface{}) map[string]interface{} {
|
||||||
nm := make(map[string]interface{})
|
nm := make(map[string]interface{})
|
||||||
|
|
||||||
for key, val := range m {
|
for key, val := range m {
|
||||||
lkey := strings.ToLower(key)
|
lkey := insensitiveopt.ToLower(key)
|
||||||
switch v := val.(type) {
|
switch v := val.(type) {
|
||||||
case map[interface{}]interface{}:
|
case map[interface{}]interface{}:
|
||||||
nm[lkey] = copyAndInsensitiviseMap(cast.ToStringMap(v))
|
nm[lkey] = copyAndInsensitiviseMap(cast.ToStringMap(v))
|
||||||
|
@ -83,7 +83,7 @@ func insensitiviseVal(val interface{}) interface{} {
|
||||||
func insensitiviseMap(m map[string]interface{}) {
|
func insensitiviseMap(m map[string]interface{}) {
|
||||||
for key, val := range m {
|
for key, val := range m {
|
||||||
val = insensitiviseVal(val)
|
val = insensitiviseVal(val)
|
||||||
lower := strings.ToLower(key)
|
lower := insensitiveopt.ToLower(key)
|
||||||
if key != lower {
|
if key != lower {
|
||||||
// remove old key (not lower-cased)
|
// remove old key (not lower-cased)
|
||||||
delete(m, key)
|
delete(m, key)
|
||||||
|
@ -159,7 +159,7 @@ func parseSizeInBytes(sizeStr string) uint {
|
||||||
if lastChar > 0 {
|
if lastChar > 0 {
|
||||||
if sizeStr[lastChar] == 'b' || sizeStr[lastChar] == 'B' {
|
if sizeStr[lastChar] == 'b' || sizeStr[lastChar] == 'B' {
|
||||||
if lastChar > 1 {
|
if lastChar > 1 {
|
||||||
switch unicode.ToLower(rune(sizeStr[lastChar-1])) {
|
switch insensitiveopt.ToLowerRune(rune(sizeStr[lastChar-1])) {
|
||||||
case 'k':
|
case 'k':
|
||||||
multiplier = 1 << 10
|
multiplier = 1 << 10
|
||||||
sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])
|
sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])
|
||||||
|
|
47
viper.go
47
viper.go
|
@ -48,6 +48,7 @@ import (
|
||||||
"github.com/spf13/viper/internal/encoding/json"
|
"github.com/spf13/viper/internal/encoding/json"
|
||||||
"github.com/spf13/viper/internal/encoding/toml"
|
"github.com/spf13/viper/internal/encoding/toml"
|
||||||
"github.com/spf13/viper/internal/encoding/yaml"
|
"github.com/spf13/viper/internal/encoding/yaml"
|
||||||
|
insensitiveopt "github.com/spf13/viper/internal/insensitiveOpt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConfigMarshalError happens when failing to marshal the configuration.
|
// ConfigMarshalError happens when failing to marshal the configuration.
|
||||||
|
@ -305,6 +306,14 @@ func Reset() {
|
||||||
SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore"}
|
SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore"}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetCaseSensitive() {
|
||||||
|
insensitiveopt.Insensitive(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetCaseInsensitive() {
|
||||||
|
insensitiveopt.Insensitive(true)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: make this lazy initialization instead
|
// TODO: make this lazy initialization instead
|
||||||
func (v *Viper) resetEncoding() {
|
func (v *Viper) resetEncoding() {
|
||||||
encoderRegistry := encoding.NewEncoderRegistry()
|
encoderRegistry := encoding.NewEncoderRegistry()
|
||||||
|
@ -699,7 +708,7 @@ func (v *Viper) searchIndexableWithPathPrefixes(source interface{}, path []strin
|
||||||
|
|
||||||
// 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 := insensitiveopt.ToLower(strings.Join(path[0:i], v.keyDelim))
|
||||||
|
|
||||||
var val interface{}
|
var val interface{}
|
||||||
switch sourceIndexable := source.(type) {
|
switch sourceIndexable := source.(type) {
|
||||||
|
@ -890,7 +899,7 @@ func GetViper() *Viper {
|
||||||
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 := insensitiveopt.ToLower(key)
|
||||||
val := v.find(lcaseKey, true)
|
val := v.find(lcaseKey, true)
|
||||||
if val == nil {
|
if val == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -950,7 +959,7 @@ func (v *Viper) Sub(key string) *Viper {
|
||||||
}
|
}
|
||||||
|
|
||||||
if reflect.TypeOf(data).Kind() == reflect.Map {
|
if reflect.TypeOf(data).Kind() == reflect.Map {
|
||||||
subv.parents = append(v.parents, strings.ToLower(key))
|
subv.parents = append(v.parents, insensitiveopt.ToLower(key))
|
||||||
subv.automaticEnvApplied = v.automaticEnvApplied
|
subv.automaticEnvApplied = v.automaticEnvApplied
|
||||||
subv.envPrefix = v.envPrefix
|
subv.envPrefix = v.envPrefix
|
||||||
subv.envKeyReplacer = v.envKeyReplacer
|
subv.envKeyReplacer = v.envKeyReplacer
|
||||||
|
@ -1189,7 +1198,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[insensitiveopt.ToLower(key)] = flag
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1206,7 +1215,7 @@ func (v *Viper) BindEnv(input ...string) error {
|
||||||
return fmt.Errorf("missing key to bind to")
|
return fmt.Errorf("missing key to bind to")
|
||||||
}
|
}
|
||||||
|
|
||||||
key := strings.ToLower(input[0])
|
key := insensitiveopt.ToLower(input[0])
|
||||||
|
|
||||||
if len(input) == 1 {
|
if len(input) == 1 {
|
||||||
v.env[key] = append(v.env[key], v.mergeWithEnvPrefix(key))
|
v.env[key] = append(v.env[key], v.mergeWithEnvPrefix(key))
|
||||||
|
@ -1423,7 +1432,7 @@ func stringToStringConv(val string) interface{} {
|
||||||
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 := insensitiveopt.ToLower(key)
|
||||||
val := v.find(lcaseKey, false)
|
val := v.find(lcaseKey, false)
|
||||||
return val != nil
|
return val != nil
|
||||||
}
|
}
|
||||||
|
@ -1450,11 +1459,11 @@ func (v *Viper) SetEnvKeyReplacer(r *strings.Replacer) {
|
||||||
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, insensitiveopt.ToLower(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Viper) registerAlias(alias string, key string) {
|
func (v *Viper) registerAlias(alias string, key string) {
|
||||||
alias = strings.ToLower(alias)
|
alias = insensitiveopt.ToLower(alias)
|
||||||
if alias != key && alias != v.realKey(key) {
|
if alias != key && alias != v.realKey(key) {
|
||||||
_, exists := v.aliases[alias]
|
_, exists := v.aliases[alias]
|
||||||
|
|
||||||
|
@ -1499,7 +1508,7 @@ func (v *Viper) realKey(key string) string {
|
||||||
func InConfig(key string) bool { return v.InConfig(key) }
|
func InConfig(key string) bool { return v.InConfig(key) }
|
||||||
|
|
||||||
func (v *Viper) InConfig(key string) bool {
|
func (v *Viper) InConfig(key string) bool {
|
||||||
lcaseKey := strings.ToLower(key)
|
lcaseKey := insensitiveopt.ToLower(key)
|
||||||
|
|
||||||
// if the requested key is an alias, then return the proper key
|
// if the requested key is an alias, then return the proper key
|
||||||
lcaseKey = v.realKey(lcaseKey)
|
lcaseKey = v.realKey(lcaseKey)
|
||||||
|
@ -1515,11 +1524,11 @@ 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(insensitiveopt.ToLower(key))
|
||||||
value = toCaseInsensitiveValue(value)
|
value = toCaseInsensitiveValue(value)
|
||||||
|
|
||||||
path := strings.Split(key, v.keyDelim)
|
path := strings.Split(key, v.keyDelim)
|
||||||
lastKey := strings.ToLower(path[len(path)-1])
|
lastKey := insensitiveopt.ToLower(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
|
||||||
|
@ -1534,11 +1543,11 @@ 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(insensitiveopt.ToLower(key))
|
||||||
value = toCaseInsensitiveValue(value)
|
value = toCaseInsensitiveValue(value)
|
||||||
|
|
||||||
path := strings.Split(key, v.keyDelim)
|
path := strings.Split(key, v.keyDelim)
|
||||||
lastKey := strings.ToLower(path[len(path)-1])
|
lastKey := insensitiveopt.ToLower(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
|
||||||
|
@ -1719,7 +1728,7 @@ func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
buf.ReadFrom(in)
|
buf.ReadFrom(in)
|
||||||
|
|
||||||
switch format := strings.ToLower(v.getConfigType()); format {
|
switch format := insensitiveopt.ToLower(v.getConfigType()); format {
|
||||||
case "yaml", "yml", "json", "toml", "hcl", "tfvars", "ini", "properties", "props", "prop", "dotenv", "env":
|
case "yaml", "yml", "json", "toml", "hcl", "tfvars", "ini", "properties", "props", "prop", "dotenv", "env":
|
||||||
err := v.decoderRegistry.Decode(format, buf.Bytes(), c)
|
err := v.decoderRegistry.Decode(format, buf.Bytes(), c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1750,9 +1759,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 := insensitiveopt.ToLower(k)
|
||||||
for mk := range m {
|
for mk := range m {
|
||||||
lmk := strings.ToLower(mk)
|
lmk := insensitiveopt.ToLower(mk)
|
||||||
if lmk == lk {
|
if lmk == lk {
|
||||||
return mk
|
return mk
|
||||||
}
|
}
|
||||||
|
@ -2031,7 +2040,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[insensitiveopt.ToLower(fullKey)] = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// recursively merge to shadow map
|
// recursively merge to shadow map
|
||||||
|
@ -2057,7 +2066,7 @@ outer:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// add key
|
// add key
|
||||||
shadow[strings.ToLower(k)] = true
|
shadow[insensitiveopt.ToLower(k)] = true
|
||||||
}
|
}
|
||||||
return shadow
|
return shadow
|
||||||
}
|
}
|
||||||
|
@ -2076,7 +2085,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 := insensitiveopt.ToLower(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
|
||||||
|
|
Loading…
Reference in a new issue