[FEATURE] it's option to set case sensitive

This commit is contained in:
zhijian.chen 2023-04-15 13:25:37 +08:00
parent adc3a873f0
commit 856e87ed6d
8 changed files with 77 additions and 33 deletions

View file

@ -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).

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View 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
}

View file

@ -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])

View file

@ -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