aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/spf13/viper/viper.go
diff options
context:
space:
mode:
authorTaras Madan <tarasmadan@google.com>2022-09-05 14:27:54 +0200
committerGitHub <noreply@github.com>2022-09-05 12:27:54 +0000
commitb2f2446b46bf02821d90ebedadae2bf7ae0e880e (patch)
tree923cf42842918d6bebca1d6bbdc08abed54d274d /vendor/github.com/spf13/viper/viper.go
parente6654faff4bcca4be92e9a8596fd4b77f747c39e (diff)
go.mod, vendor: update (#3358)
* go.mod, vendor: remove unnecessary dependencies Commands: 1. go mod tidy 2. go mod vendor * go.mod, vendor: update cloud.google.com/go Commands: 1. go get -u cloud.google.com/go 2. go mod tidy 3. go mod vendor * go.mod, vendor: update cloud.google.com/* Commands: 1. go get -u cloud.google.com/storage cloud.google.com/logging 2. go mod tidy 3. go mod vendor * go.mod, .golangci.yml, vendor: update *lint* Commands: 1. go get -u golang.org/x/tools github.com/golangci/golangci-lint@v1.47.0 2. go mod tidy 3. go mod vendor 4. edit .golangci.yml to suppress new errors (resolved in the same PR later) * all: fix lint errors hash.go: copy() recommended by gosimple parse.go: ent is never nil verifier.go: signal.Notify() with unbuffered channel is bad. Have no idea why. * .golangci.yml: adjust godot rules check-all is deprecated, but still work if you're hesitating too - I'll remove this commit
Diffstat (limited to 'vendor/github.com/spf13/viper/viper.go')
-rw-r--r--vendor/github.com/spf13/viper/viper.go641
1 files changed, 370 insertions, 271 deletions
diff --git a/vendor/github.com/spf13/viper/viper.go b/vendor/github.com/spf13/viper/viper.go
index 405dc20fe..a3812e92f 100644
--- a/vendor/github.com/spf13/viper/viper.go
+++ b/vendor/github.com/spf13/viper/viper.go
@@ -22,7 +22,6 @@ package viper
import (
"bytes"
"encoding/csv"
- "encoding/json"
"errors"
"fmt"
"io"
@@ -30,23 +29,25 @@ import (
"os"
"path/filepath"
"reflect"
+ "strconv"
"strings"
"sync"
"time"
"github.com/fsnotify/fsnotify"
- "github.com/hashicorp/hcl"
- "github.com/hashicorp/hcl/hcl/printer"
- "github.com/magiconair/properties"
"github.com/mitchellh/mapstructure"
- "github.com/pelletier/go-toml"
"github.com/spf13/afero"
"github.com/spf13/cast"
- jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/pflag"
- "github.com/subosito/gotenv"
- "gopkg.in/ini.v1"
- "gopkg.in/yaml.v2"
+
+ "github.com/spf13/viper/internal/encoding"
+ "github.com/spf13/viper/internal/encoding/dotenv"
+ "github.com/spf13/viper/internal/encoding/hcl"
+ "github.com/spf13/viper/internal/encoding/ini"
+ "github.com/spf13/viper/internal/encoding/javaproperties"
+ "github.com/spf13/viper/internal/encoding/json"
+ "github.com/spf13/viper/internal/encoding/toml"
+ "github.com/spf13/viper/internal/encoding/yaml"
)
// ConfigMarshalError happens when failing to marshal the configuration.
@@ -175,6 +176,8 @@ func DecodeHook(hook mapstructure.DecodeHookFunc) DecoderConfigOption {
// "user": "root",
// "endpoint": "https://localhost"
// }
+//
+// Note: Vipers are not safe for concurrent Get() and Set() operations.
type Viper struct {
// Delimiter that separates a list of keys
// used to access a nested value in one go
@@ -196,6 +199,9 @@ type Viper struct {
configPermissions os.FileMode
envPrefix string
+ // Specific commands for ini parsing
+ iniLoadOptions ini.LoadOptions
+
automaticEnvApplied bool
envKeyReplacer StringReplacer
allowEmptyEnv bool
@@ -205,15 +211,17 @@ type Viper struct {
defaults map[string]interface{}
kvstore map[string]interface{}
pflags map[string]FlagValue
- env map[string]string
+ env map[string][]string
aliases map[string]string
typeByDefValue bool
- // Store read properties on the object so that we can write back in order with comments.
- // This will only be used if the configuration read is a properties file.
- properties *properties.Properties
-
onConfigChange func(fsnotify.Event)
+
+ logger Logger
+
+ // TODO: should probably be protected with a mutex
+ encoderRegistry *encoding.EncoderRegistry
+ decoderRegistry *encoding.DecoderRegistry
}
// New returns an initialized Viper instance.
@@ -221,16 +229,19 @@ func New() *Viper {
v := new(Viper)
v.keyDelim = "."
v.configName = "config"
- v.configPermissions = os.FileMode(0644)
+ v.configPermissions = os.FileMode(0o644)
v.fs = afero.NewOsFs()
v.config = make(map[string]interface{})
v.override = make(map[string]interface{})
v.defaults = make(map[string]interface{})
v.kvstore = make(map[string]interface{})
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.typeByDefValue = false
+ v.logger = jwwLogger{}
+
+ v.resetEncoding()
return v
}
@@ -278,6 +289,8 @@ func NewWithOptions(opts ...Option) *Viper {
opt.apply(v)
}
+ v.resetEncoding()
+
return v
}
@@ -286,10 +299,88 @@ func NewWithOptions(opts ...Option) *Viper {
// can use it in their testing as well.
func Reset() {
v = New()
- SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "dotenv", "env", "ini"}
+ SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}
SupportedRemoteProviders = []string{"etcd", "consul", "firestore"}
}
+// TODO: make this lazy initialization instead
+func (v *Viper) resetEncoding() {
+ encoderRegistry := encoding.NewEncoderRegistry()
+ decoderRegistry := encoding.NewDecoderRegistry()
+
+ {
+ codec := yaml.Codec{}
+
+ encoderRegistry.RegisterEncoder("yaml", codec)
+ decoderRegistry.RegisterDecoder("yaml", codec)
+
+ encoderRegistry.RegisterEncoder("yml", codec)
+ decoderRegistry.RegisterDecoder("yml", codec)
+ }
+
+ {
+ codec := json.Codec{}
+
+ encoderRegistry.RegisterEncoder("json", codec)
+ decoderRegistry.RegisterDecoder("json", codec)
+ }
+
+ {
+ codec := toml.Codec{}
+
+ encoderRegistry.RegisterEncoder("toml", codec)
+ decoderRegistry.RegisterDecoder("toml", codec)
+ }
+
+ {
+ codec := hcl.Codec{}
+
+ encoderRegistry.RegisterEncoder("hcl", codec)
+ decoderRegistry.RegisterDecoder("hcl", codec)
+
+ encoderRegistry.RegisterEncoder("tfvars", codec)
+ decoderRegistry.RegisterDecoder("tfvars", codec)
+ }
+
+ {
+ codec := ini.Codec{
+ KeyDelimiter: v.keyDelim,
+ LoadOptions: v.iniLoadOptions,
+ }
+
+ encoderRegistry.RegisterEncoder("ini", codec)
+ decoderRegistry.RegisterDecoder("ini", codec)
+ }
+
+ {
+ codec := &javaproperties.Codec{
+ KeyDelimiter: v.keyDelim,
+ }
+
+ encoderRegistry.RegisterEncoder("properties", codec)
+ decoderRegistry.RegisterDecoder("properties", codec)
+
+ encoderRegistry.RegisterEncoder("props", codec)
+ decoderRegistry.RegisterDecoder("props", codec)
+
+ encoderRegistry.RegisterEncoder("prop", codec)
+ decoderRegistry.RegisterDecoder("prop", codec)
+ }
+
+ {
+ codec := &dotenv.Codec{}
+
+ encoderRegistry.RegisterEncoder("dotenv", codec)
+ decoderRegistry.RegisterDecoder("dotenv", codec)
+
+ encoderRegistry.RegisterEncoder("env", codec)
+ decoderRegistry.RegisterDecoder("env", codec)
+ }
+
+ v.encoderRegistry = encoderRegistry
+ v.decoderRegistry = decoderRegistry
+}
+
type defaultRemoteProvider struct {
provider string
endpoint string
@@ -325,7 +416,7 @@ type RemoteProvider interface {
}
// SupportedExts are universally supported extensions.
-var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "dotenv", "env", "ini"}
+var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}
// SupportedRemoteProviders are universally supported remote providers.
var SupportedRemoteProviders = []string{"etcd", "consul", "firestore"}
@@ -341,7 +432,7 @@ func (v *Viper) WatchConfig() {
initWG := sync.WaitGroup{}
initWG.Add(1)
go func() {
- watcher, err := fsnotify.NewWatcher()
+ watcher, err := newWatcher()
if err != nil {
log.Fatal(err)
}
@@ -385,7 +476,7 @@ func (v *Viper) WatchConfig() {
v.onConfigChange(event)
}
} else if filepath.Clean(event.Name) == configFile &&
- event.Op&fsnotify.Remove&fsnotify.Remove != 0 {
+ event.Op&fsnotify.Remove != 0 {
eventsWG.Done()
return
}
@@ -409,6 +500,7 @@ func (v *Viper) WatchConfig() {
// SetConfigFile explicitly defines the path, name and extension of the config file.
// Viper will use this and not check any of the config paths.
func SetConfigFile(in string) { v.SetConfigFile(in) }
+
func (v *Viper) SetConfigFile(in string) {
if in != "" {
v.configFile = in
@@ -419,6 +511,7 @@ func (v *Viper) SetConfigFile(in string) {
// E.g. if your prefix is "spf", the env registry will look for env
// variables that start with "SPF_".
func SetEnvPrefix(in string) { v.SetEnvPrefix(in) }
+
func (v *Viper) SetEnvPrefix(in string) {
if in != "" {
v.envPrefix = in
@@ -437,6 +530,7 @@ func (v *Viper) mergeWithEnvPrefix(in string) string {
// but empty environment variables as valid values instead of falling back.
// For backward compatibility reasons this is false by default.
func AllowEmptyEnv(allowEmptyEnv bool) { v.AllowEmptyEnv(allowEmptyEnv) }
+
func (v *Viper) AllowEmptyEnv(allowEmptyEnv bool) {
v.allowEmptyEnv = allowEmptyEnv
}
@@ -465,10 +559,12 @@ func (v *Viper) ConfigFileUsed() string { return v.configFile }
// AddConfigPath adds a path for Viper to search for the config file in.
// Can be called multiple times to define multiple search paths.
func AddConfigPath(in string) { v.AddConfigPath(in) }
+
func (v *Viper) AddConfigPath(in string) {
if in != "" {
- absin := absPathify(in)
- jww.INFO.Println("adding", absin, "to paths to search")
+ absin := absPathify(v.logger, in)
+
+ v.logger.Info("adding path to search paths", "path", absin)
if !stringInSlice(absin, v.configPaths) {
v.configPaths = append(v.configPaths, absin)
}
@@ -486,12 +582,14 @@ func (v *Viper) AddConfigPath(in string) {
func AddRemoteProvider(provider, endpoint, path string) error {
return v.AddRemoteProvider(provider, endpoint, path)
}
+
func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error {
if !stringInSlice(provider, SupportedRemoteProviders) {
return UnsupportedRemoteProviderError(provider)
}
if provider != "" && endpoint != "" {
- jww.INFO.Printf("adding %s:%s to remote provider list", provider, endpoint)
+ v.logger.Info("adding remote provider", "provider", provider, "endpoint", endpoint)
+
rp := &defaultRemoteProvider{
endpoint: endpoint,
provider: provider,
@@ -523,7 +621,8 @@ func (v *Viper) AddSecureRemoteProvider(provider, endpoint, path, secretkeyring
return UnsupportedRemoteProviderError(provider)
}
if provider != "" && endpoint != "" {
- jww.INFO.Printf("adding %s:%s to remote provider list", provider, endpoint)
+ v.logger.Info("adding remote provider", "provider", provider, "endpoint", endpoint)
+
rp := &defaultRemoteProvider{
endpoint: endpoint,
provider: provider,
@@ -577,9 +676,9 @@ func (v *Viper) searchMap(source map[string]interface{}, path []string) interfac
return nil
}
-// searchMapWithPathPrefixes recursively searches for a value for path in source map.
+// searchIndexableWithPathPrefixes recursively searches for a value for path in source map/slice.
//
-// While searchMap() considers each path element as a single map key, this
+// While searchMap() considers each path element as a single map key or slice index, this
// function searches for, and prioritizes, merged path elements.
// e.g., if in the source, "foo" is defined with a sub-key "bar", and "foo.bar"
// is also defined, this latter value is returned for path ["foo", "bar"].
@@ -588,7 +687,7 @@ func (v *Viper) searchMap(source map[string]interface{}, path []string) interfac
// in their keys).
//
// Note: This assumes that the path entries and map keys are lower cased.
-func (v *Viper) searchMapWithPathPrefixes(source map[string]interface{}, path []string) interface{} {
+func (v *Viper) searchIndexableWithPathPrefixes(source interface{}, path []string) interface{} {
if len(path) == 0 {
return source
}
@@ -597,28 +696,15 @@ func (v *Viper) searchMapWithPathPrefixes(source map[string]interface{}, path []
for i := len(path); i > 0; i-- {
prefixKey := strings.ToLower(strings.Join(path[0:i], v.keyDelim))
- next, ok := source[prefixKey]
- if ok {
- // Fast path
- if i == len(path) {
- return next
- }
-
- // Nested case
- var val interface{}
- switch next.(type) {
- case map[interface{}]interface{}:
- val = v.searchMapWithPathPrefixes(cast.ToStringMap(next), path[i:])
- case map[string]interface{}:
- // Type assertion is safe here since it is only reached
- // if the type of `next` is the same as the type being asserted
- val = v.searchMapWithPathPrefixes(next.(map[string]interface{}), path[i:])
- default:
- // got a value but nested key expected, do nothing and look for next prefix
- }
- if val != nil {
- return val
- }
+ var val interface{}
+ switch sourceIndexable := source.(type) {
+ case []interface{}:
+ val = v.searchSliceWithPathPrefixes(sourceIndexable, prefixKey, i, path)
+ case map[string]interface{}:
+ val = v.searchMapWithPathPrefixes(sourceIndexable, prefixKey, i, path)
+ }
+ if val != nil {
+ return val
}
}
@@ -626,6 +712,76 @@ func (v *Viper) searchMapWithPathPrefixes(source map[string]interface{}, path []
return nil
}
+// searchSliceWithPathPrefixes searches for a value for path in sourceSlice
+//
+// This function is part of the searchIndexableWithPathPrefixes recurring search and
+// should not be called directly from functions other than searchIndexableWithPathPrefixes.
+func (v *Viper) searchSliceWithPathPrefixes(
+ sourceSlice []interface{},
+ prefixKey string,
+ pathIndex int,
+ path []string,
+) interface{} {
+ // if the prefixKey is not a number or it is out of bounds of the slice
+ index, err := strconv.Atoi(prefixKey)
+ if err != nil || len(sourceSlice) <= index {
+ return nil
+ }
+
+ next := sourceSlice[index]
+
+ // Fast path
+ if pathIndex == len(path) {
+ return next
+ }
+
+ switch n := next.(type) {
+ case map[interface{}]interface{}:
+ return v.searchIndexableWithPathPrefixes(cast.ToStringMap(n), path[pathIndex:])
+ case map[string]interface{}, []interface{}:
+ return v.searchIndexableWithPathPrefixes(n, path[pathIndex:])
+ default:
+ // got a value but nested key expected, do nothing and look for next prefix
+ }
+
+ // not found
+ return nil
+}
+
+// searchMapWithPathPrefixes searches for a value for path in sourceMap
+//
+// This function is part of the searchIndexableWithPathPrefixes recurring search and
+// should not be called directly from functions other than searchIndexableWithPathPrefixes.
+func (v *Viper) searchMapWithPathPrefixes(
+ sourceMap map[string]interface{},
+ prefixKey string,
+ pathIndex int,
+ path []string,
+) interface{} {
+ next, ok := sourceMap[prefixKey]
+ if !ok {
+ return nil
+ }
+
+ // Fast path
+ if pathIndex == len(path) {
+ return next
+ }
+
+ // Nested case
+ switch n := next.(type) {
+ case map[interface{}]interface{}:
+ return v.searchIndexableWithPathPrefixes(cast.ToStringMap(n), path[pathIndex:])
+ case map[string]interface{}, []interface{}:
+ return v.searchIndexableWithPathPrefixes(n, path[pathIndex:])
+ default:
+ // got a value but nested key expected, do nothing and look for next prefix
+ }
+
+ // not found
+ return nil
+}
+
// isPathShadowedInDeepMap makes sure the given path is not shadowed somewhere
// on its path in the map.
// e.g., if "foo.bar" has a value in the given map, it “shadows”
@@ -706,6 +862,7 @@ func (v *Viper) isPathShadowedInAutoEnv(path []string) string {
//
// "a b c"
func SetTypeByDefaultValue(enable bool) { v.SetTypeByDefaultValue(enable) }
+
func (v *Viper) SetTypeByDefaultValue(enable bool) {
v.typeByDefValue = enable
}
@@ -723,6 +880,7 @@ func GetViper() *Viper {
//
// Get returns an interface. For a specific value use one of the Get____ methods.
func Get(key string) interface{} { return v.Get(key) }
+
func (v *Viper) Get(key string) interface{} {
lcaseKey := strings.ToLower(key)
val := v.find(lcaseKey, true)
@@ -773,6 +931,7 @@ func (v *Viper) Get(key string) interface{} {
// Sub returns new Viper instance representing a sub tree of this instance.
// Sub is case-insensitive for a key.
func Sub(key string) *Viper { return v.Sub(key) }
+
func (v *Viper) Sub(key string) *Viper {
subv := New()
data := v.Get(key)
@@ -789,96 +948,112 @@ func (v *Viper) Sub(key string) *Viper {
// GetString returns the value associated with the key as a string.
func GetString(key string) string { return v.GetString(key) }
+
func (v *Viper) GetString(key string) string {
return cast.ToString(v.Get(key))
}
// GetBool returns the value associated with the key as a boolean.
func GetBool(key string) bool { return v.GetBool(key) }
+
func (v *Viper) GetBool(key string) bool {
return cast.ToBool(v.Get(key))
}
// GetInt returns the value associated with the key as an integer.
func GetInt(key string) int { return v.GetInt(key) }
+
func (v *Viper) GetInt(key string) int {
return cast.ToInt(v.Get(key))
}
// GetInt32 returns the value associated with the key as an integer.
func GetInt32(key string) int32 { return v.GetInt32(key) }
+
func (v *Viper) GetInt32(key string) int32 {
return cast.ToInt32(v.Get(key))
}
// GetInt64 returns the value associated with the key as an integer.
func GetInt64(key string) int64 { return v.GetInt64(key) }
+
func (v *Viper) GetInt64(key string) int64 {
return cast.ToInt64(v.Get(key))
}
// GetUint returns the value associated with the key as an unsigned integer.
func GetUint(key string) uint { return v.GetUint(key) }
+
func (v *Viper) GetUint(key string) uint {
return cast.ToUint(v.Get(key))
}
// GetUint32 returns the value associated with the key as an unsigned integer.
func GetUint32(key string) uint32 { return v.GetUint32(key) }
+
func (v *Viper) GetUint32(key string) uint32 {
return cast.ToUint32(v.Get(key))
}
// GetUint64 returns the value associated with the key as an unsigned integer.
func GetUint64(key string) uint64 { return v.GetUint64(key) }
+
func (v *Viper) GetUint64(key string) uint64 {
return cast.ToUint64(v.Get(key))
}
// GetFloat64 returns the value associated with the key as a float64.
func GetFloat64(key string) float64 { return v.GetFloat64(key) }
+
func (v *Viper) GetFloat64(key string) float64 {
return cast.ToFloat64(v.Get(key))
}
// GetTime returns the value associated with the key as time.
func GetTime(key string) time.Time { return v.GetTime(key) }
+
func (v *Viper) GetTime(key string) time.Time {
return cast.ToTime(v.Get(key))
}
// GetDuration returns the value associated with the key as a duration.
func GetDuration(key string) time.Duration { return v.GetDuration(key) }
+
func (v *Viper) GetDuration(key string) time.Duration {
return cast.ToDuration(v.Get(key))
}
// GetIntSlice returns the value associated with the key as a slice of int values.
func GetIntSlice(key string) []int { return v.GetIntSlice(key) }
+
func (v *Viper) GetIntSlice(key string) []int {
return cast.ToIntSlice(v.Get(key))
}
// GetStringSlice returns the value associated with the key as a slice of strings.
func GetStringSlice(key string) []string { return v.GetStringSlice(key) }
+
func (v *Viper) GetStringSlice(key string) []string {
return cast.ToStringSlice(v.Get(key))
}
// GetStringMap returns the value associated with the key as a map of interfaces.
func GetStringMap(key string) map[string]interface{} { return v.GetStringMap(key) }
+
func (v *Viper) GetStringMap(key string) map[string]interface{} {
return cast.ToStringMap(v.Get(key))
}
// GetStringMapString returns the value associated with the key as a map of strings.
func GetStringMapString(key string) map[string]string { return v.GetStringMapString(key) }
+
func (v *Viper) GetStringMapString(key string) map[string]string {
return cast.ToStringMapString(v.Get(key))
}
// GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings.
func GetStringMapStringSlice(key string) map[string][]string { return v.GetStringMapStringSlice(key) }
+
func (v *Viper) GetStringMapStringSlice(key string) map[string][]string {
return cast.ToStringMapStringSlice(v.Get(key))
}
@@ -886,6 +1061,7 @@ func (v *Viper) GetStringMapStringSlice(key string) map[string][]string {
// GetSizeInBytes returns the size of the value associated with the given key
// in bytes.
func GetSizeInBytes(key string) uint { return v.GetSizeInBytes(key) }
+
func (v *Viper) GetSizeInBytes(key string) uint {
sizeStr := cast.ToString(v.Get(key))
return parseSizeInBytes(sizeStr)
@@ -895,6 +1071,7 @@ func (v *Viper) GetSizeInBytes(key string) uint {
func UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConfigOption) error {
return v.UnmarshalKey(key, rawVal, opts...)
}
+
func (v *Viper) UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConfigOption) error {
return decode(v.Get(key), defaultDecoderConfig(rawVal, opts...))
}
@@ -904,6 +1081,7 @@ func (v *Viper) UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConf
func Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error {
return v.Unmarshal(rawVal, opts...)
}
+
func (v *Viper) Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error {
return decode(v.AllSettings(), defaultDecoderConfig(rawVal, opts...))
}
@@ -940,6 +1118,7 @@ func decode(input interface{}, config *mapstructure.DecoderConfig) error {
func UnmarshalExact(rawVal interface{}, opts ...DecoderConfigOption) error {
return v.UnmarshalExact(rawVal, opts...)
}
+
func (v *Viper) UnmarshalExact(rawVal interface{}, opts ...DecoderConfigOption) error {
config := defaultDecoderConfig(rawVal, opts...)
config.ErrorUnused = true
@@ -950,6 +1129,7 @@ func (v *Viper) UnmarshalExact(rawVal interface{}, opts ...DecoderConfigOption)
// BindPFlags binds a full flag set to the configuration, using each flag's long
// name as the config key.
func BindPFlags(flags *pflag.FlagSet) error { return v.BindPFlags(flags) }
+
func (v *Viper) BindPFlags(flags *pflag.FlagSet) error {
return v.BindFlagValues(pflagValueSet{flags})
}
@@ -961,13 +1141,18 @@ func (v *Viper) BindPFlags(flags *pflag.FlagSet) error {
// Viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
//
func BindPFlag(key string, flag *pflag.Flag) error { return v.BindPFlag(key, flag) }
+
func (v *Viper) BindPFlag(key string, flag *pflag.Flag) error {
+ if flag == nil {
+ return fmt.Errorf("flag for %q is nil", key)
+ }
return v.BindFlagValue(key, pflagValue{flag})
}
// BindFlagValues binds a full FlagValue set to the configuration, using each flag's long
// name as the config key.
func BindFlagValues(flags FlagValueSet) error { return v.BindFlagValues(flags) }
+
func (v *Viper) BindFlagValues(flags FlagValueSet) (err error) {
flags.VisitAll(func(flag FlagValue) {
if err = v.BindFlagValue(flag.Name(), flag); err != nil {
@@ -979,6 +1164,7 @@ func (v *Viper) BindFlagValues(flags FlagValueSet) (err error) {
// BindFlagValue binds a specific key to a FlagValue.
func BindFlagValue(key string, flag FlagValue) error { return v.BindFlagValue(key, flag) }
+
func (v *Viper) BindFlagValue(key string, flag FlagValue) error {
if flag == nil {
return fmt.Errorf("flag for %q is nil", key)
@@ -990,27 +1176,38 @@ func (v *Viper) BindFlagValue(key string, flag FlagValue) error {
// 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.
+// If more arguments are provided, they will represent the env variable names that
+// should bind to this key and will be taken in the specified order.
// EnvPrefix will be used when set when env name is not provided.
func BindEnv(input ...string) error { return v.BindEnv(input...) }
+
func (v *Viper) BindEnv(input ...string) error {
- var key, envkey string
if len(input) == 0 {
return fmt.Errorf("missing key to bind to")
}
- key = strings.ToLower(input[0])
+ key := strings.ToLower(input[0])
if len(input) == 1 {
- envkey = v.mergeWithEnvPrefix(key)
+ v.env[key] = append(v.env[key], v.mergeWithEnvPrefix(key))
} else {
- envkey = input[1]
+ v.env[key] = append(v.env[key], input[1:]...)
}
- v.env[key] = envkey
-
return nil
}
+// MustBindEnv wraps BindEnv in a panic.
+// If there is an error binding an environment variable, MustBindEnv will
+// panic.
+func MustBindEnv(input ...string) { v.MustBindEnv(input...) }
+
+func (v *Viper) MustBindEnv(input ...string) {
+ if err := v.BindEnv(input...); err != nil {
+ panic(fmt.Sprintf("error while binding environment variable: %v", err))
+ }
+}
+
// Given a key, find the value.
//
// Viper will check to see if an alias exists first.
@@ -1055,7 +1252,7 @@ func (v *Viper) find(lcaseKey string, flagDefault bool) interface{} {
return cast.ToInt(flag.ValueString())
case "bool":
return cast.ToBool(flag.ValueString())
- case "stringSlice":
+ case "stringSlice", "stringArray":
s := strings.TrimPrefix(flag.ValueString(), "[")
s = strings.TrimSuffix(s, "]")
res, _ := readAsCSV(s)
@@ -1086,10 +1283,12 @@ func (v *Viper) find(lcaseKey string, flagDefault bool) interface{} {
return nil
}
}
- envkey, exists := v.env[lcaseKey]
+ envkeys, exists := v.env[lcaseKey]
if exists {
- if val, ok := v.getEnv(envkey); ok {
- return val
+ for _, envkey := range envkeys {
+ if val, ok := v.getEnv(envkey); ok {
+ return val
+ }
}
}
if nested && v.isPathShadowedInFlatMap(path, v.env) != "" {
@@ -1097,7 +1296,7 @@ func (v *Viper) find(lcaseKey string, flagDefault bool) interface{} {
}
// Config file next
- val = v.searchMapWithPathPrefixes(v.config, path)
+ val = v.searchIndexableWithPathPrefixes(v.config, path)
if val != nil {
return val
}
@@ -1132,7 +1331,7 @@ func (v *Viper) find(lcaseKey string, flagDefault bool) interface{} {
return cast.ToInt(flag.ValueString())
case "bool":
return cast.ToBool(flag.ValueString())
- case "stringSlice":
+ case "stringSlice", "stringArray":
s := strings.TrimPrefix(flag.ValueString(), "[")
s = strings.TrimSuffix(s, "]")
res, _ := readAsCSV(s)
@@ -1190,15 +1389,17 @@ func stringToStringConv(val string) interface{} {
// IsSet checks to see if the key has been set in any of the data locations.
// IsSet is case-insensitive for a key.
func IsSet(key string) bool { return v.IsSet(key) }
+
func (v *Viper) IsSet(key string) bool {
lcaseKey := strings.ToLower(key)
val := v.find(lcaseKey, false)
return val != nil
}
-// AutomaticEnv has Viper check ENV variables for all.
-// keys set in config, default & flags
+// AutomaticEnv makes Viper check if environment variables match any of the existing keys
+// (config, default or flags). If matching env vars are found, they are loaded into Viper.
func AutomaticEnv() { v.AutomaticEnv() }
+
func (v *Viper) AutomaticEnv() {
v.automaticEnvApplied = true
}
@@ -1207,6 +1408,7 @@ func (v *Viper) AutomaticEnv() {
// Useful for mapping an environmental variable to a key that does
// not match it.
func SetEnvKeyReplacer(r *strings.Replacer) { v.SetEnvKeyReplacer(r) }
+
func (v *Viper) SetEnvKeyReplacer(r *strings.Replacer) {
v.envKeyReplacer = r
}
@@ -1214,6 +1416,7 @@ func (v *Viper) SetEnvKeyReplacer(r *strings.Replacer) {
// RegisterAlias creates an alias that provides another accessor for the same key.
// This enables one to change a name without breaking the application.
func RegisterAlias(alias string, key string) { v.RegisterAlias(alias, key) }
+
func (v *Viper) RegisterAlias(alias string, key string) {
v.registerAlias(alias, strings.ToLower(key))
}
@@ -1246,14 +1449,15 @@ func (v *Viper) registerAlias(alias string, key string) {
v.aliases[alias] = key
}
} else {
- jww.WARN.Println("Creating circular reference alias", alias, key, v.realKey(key))
+ v.logger.Warn("creating circular reference alias", "alias", alias, "key", key, "real_key", v.realKey(key))
}
}
func (v *Viper) realKey(key string) string {
newkey, exists := v.aliases[key]
if exists {
- jww.DEBUG.Println("Alias", key, "to", newkey)
+ v.logger.Debug("key is an alias", "alias", key, "to", newkey)
+
return v.realKey(newkey)
}
return key
@@ -1261,18 +1465,22 @@ func (v *Viper) realKey(key string) string {
// InConfig checks to see if the given key (or an alias) is in the config file.
func InConfig(key string) bool { return v.InConfig(key) }
+
func (v *Viper) InConfig(key string) bool {
+ lcaseKey := strings.ToLower(key)
+
// if the requested key is an alias, then return the proper key
- key = v.realKey(key)
+ lcaseKey = v.realKey(lcaseKey)
+ path := strings.Split(lcaseKey, v.keyDelim)
- _, exists := v.config[key]
- return exists
+ return v.searchIndexableWithPathPrefixes(v.config, path) != nil
}
// 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 SetDefault(key string, value interface{}) { v.SetDefault(key, value) }
+
func (v *Viper) SetDefault(key string, value interface{}) {
// If alias passed in, then set the proper default
key = v.realKey(strings.ToLower(key))
@@ -1291,6 +1499,7 @@ func (v *Viper) SetDefault(key string, value interface{}) {
// Will be used instead of values obtained via
// flags, config file, ENV, default, or key/value store.
func Set(key string, value interface{}) { v.Set(key, value) }
+
func (v *Viper) Set(key string, value interface{}) {
// If alias passed in, then set the proper override
key = v.realKey(strings.ToLower(key))
@@ -1307,8 +1516,9 @@ func (v *Viper) Set(key string, value interface{}) {
// ReadInConfig will discover and load the configuration file from disk
// and key/value stores, searching in one of the defined paths.
func ReadInConfig() error { return v.ReadInConfig() }
+
func (v *Viper) ReadInConfig() error {
- jww.INFO.Println("Attempting to read in config file")
+ v.logger.Info("attempting to read in config file")
filename, err := v.getConfigFile()
if err != nil {
return err
@@ -1318,7 +1528,7 @@ func (v *Viper) ReadInConfig() error {
return UnsupportedConfigError(v.getConfigType())
}
- jww.DEBUG.Println("Reading file: ", filename)
+ v.logger.Debug("reading file", "file", filename)
file, err := afero.ReadFile(v.fs, filename)
if err != nil {
return err
@@ -1337,8 +1547,9 @@ func (v *Viper) ReadInConfig() error {
// MergeInConfig merges a new configuration with an existing config.
func MergeInConfig() error { return v.MergeInConfig() }
+
func (v *Viper) MergeInConfig() error {
- jww.INFO.Println("Attempting to merge in config file")
+ v.logger.Info("attempting to merge in config file")
filename, err := v.getConfigFile()
if err != nil {
return err
@@ -1359,6 +1570,7 @@ func (v *Viper) MergeInConfig() error {
// ReadConfig will read a configuration file, setting existing keys to nil if the
// key does not exist in the file.
func ReadConfig(in io.Reader) error { return v.ReadConfig(in) }
+
func (v *Viper) ReadConfig(in io.Reader) error {
v.config = make(map[string]interface{})
return v.unmarshalReader(in, v.config)
@@ -1366,6 +1578,7 @@ func (v *Viper) ReadConfig(in io.Reader) error {
// MergeConfig merges a new configuration with an existing config.
func MergeConfig(in io.Reader) error { return v.MergeConfig(in) }
+
func (v *Viper) MergeConfig(in io.Reader) error {
cfg := make(map[string]interface{})
if err := v.unmarshalReader(in, cfg); err != nil {
@@ -1377,6 +1590,7 @@ func (v *Viper) MergeConfig(in io.Reader) error {
// MergeConfigMap merges the configuration from the map given with an existing config.
// Note that the map given may be modified.
func MergeConfigMap(cfg map[string]interface{}) error { return v.MergeConfigMap(cfg) }
+
func (v *Viper) MergeConfigMap(cfg map[string]interface{}) error {
if v.config == nil {
v.config = make(map[string]interface{})
@@ -1388,6 +1602,7 @@ func (v *Viper) MergeConfigMap(cfg map[string]interface{}) error {
// WriteConfig writes the current configuration to a file.
func WriteConfig() error { return v.WriteConfig() }
+
func (v *Viper) WriteConfig() error {
filename, err := v.getConfigFile()
if err != nil {
@@ -1398,6 +1613,7 @@ func (v *Viper) WriteConfig() error {
// SafeWriteConfig writes current configuration to file only if the file does not exist.
func SafeWriteConfig() error { return v.SafeWriteConfig() }
+
func (v *Viper) SafeWriteConfig() error {
if len(v.configPaths) < 1 {
return errors.New("missing configuration for 'configPath'")
@@ -1407,12 +1623,14 @@ func (v *Viper) SafeWriteConfig() error {
// WriteConfigAs writes current configuration to a given filename.
func WriteConfigAs(filename string) error { return v.WriteConfigAs(filename) }
+
func (v *Viper) WriteConfigAs(filename string) error {
return v.writeConfig(filename, true)
}
// SafeWriteConfigAs writes current configuration to a given filename if it does not exist.
func SafeWriteConfigAs(filename string) error { return v.SafeWriteConfigAs(filename) }
+
func (v *Viper) SafeWriteConfigAs(filename string) error {
alreadyExists, err := afero.Exists(v.fs, filename)
if alreadyExists && err == nil {
@@ -1422,11 +1640,12 @@ func (v *Viper) SafeWriteConfigAs(filename string) error {
}
func (v *Viper) writeConfig(filename string, force bool) error {
- jww.INFO.Println("Attempting to write configuration to file.")
+ v.logger.Info("attempting to write configuration to file")
+
var configType string
ext := filepath.Ext(filename)
- if ext != "" {
+ if ext != "" && ext != filepath.Base(filename) {
configType = ext[1:]
} else {
configType = v.configType
@@ -1463,81 +1682,17 @@ func (v *Viper) writeConfig(filename string, force bool) error {
func unmarshalReader(in io.Reader, c map[string]interface{}) error {
return v.unmarshalReader(in, c)
}
+
func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error {
buf := new(bytes.Buffer)
buf.ReadFrom(in)
- switch strings.ToLower(v.getConfigType()) {
- case "yaml", "yml":
- if err := yaml.Unmarshal(buf.Bytes(), &c); err != nil {
- return ConfigParseError{err}
- }
-
- case "json":
- if err := json.Unmarshal(buf.Bytes(), &c); err != nil {
- return ConfigParseError{err}
- }
-
- case "hcl":
- obj, err := hcl.Parse(buf.String())
+ switch format := strings.ToLower(v.getConfigType()); format {
+ case "yaml", "yml", "json", "toml", "hcl", "tfvars", "ini", "properties", "props", "prop", "dotenv", "env":
+ err := v.decoderRegistry.Decode(format, buf.Bytes(), c)
if err != nil {
return ConfigParseError{err}
}
- if err = hcl.DecodeObject(&c, obj); err != nil {
- return ConfigParseError{err}
- }
-
- case "toml":
- tree, err := toml.LoadReader(buf)
- if err != nil {
- return ConfigParseError{err}
- }
- tmap := tree.ToMap()
- for k, v := range tmap {
- c[k] = v
- }
-
- case "dotenv", "env":
- env, err := gotenv.StrictParse(buf)
- if err != nil {
- return ConfigParseError{err}
- }
- for k, v := range env {
- c[k] = v
- }
-
- case "properties", "props", "prop":
- v.properties = properties.NewProperties()
- var err error
- if v.properties, err = properties.Load(buf.Bytes(), properties.UTF8); err != nil {
- return ConfigParseError{err}
- }
- for _, key := range v.properties.Keys() {
- value, _ := v.properties.Get(key)
- // recursively build nested maps
- path := strings.Split(key, ".")
- lastKey := strings.ToLower(path[len(path)-1])
- deepestMap := deepSearch(c, path[0:len(path)-1])
- // set innermost value
- deepestMap[lastKey] = value
- }
-
- case "ini":
- cfg := ini.Empty()
- err := cfg.Append(buf.Bytes())
- if err != nil {
- return ConfigParseError{err}
- }
- sections := cfg.Sections()
- for i := 0; i < len(sections); i++ {
- section := sections[i]
- keys := section.Keys()
- for j := 0; j < len(keys); j++ {
- key := keys[j]
- value := cfg.Section(section.Name()).Key(key.Name()).String()
- c[section.Name()+"."+key.Name()] = value
- }
- }
}
insensitiviseMap(c)
@@ -1548,92 +1703,16 @@ func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error {
func (v *Viper) marshalWriter(f afero.File, configType string) error {
c := v.AllSettings()
switch configType {
- case "json":
- b, err := json.MarshalIndent(c, "", " ")
- if err != nil {
- return ConfigMarshalError{err}
- }
- _, err = f.WriteString(string(b))
+ case "yaml", "yml", "json", "toml", "hcl", "tfvars", "ini", "prop", "props", "properties", "dotenv", "env":
+ b, err := v.encoderRegistry.Encode(configType, c)
if err != nil {
return ConfigMarshalError{err}
}
- case "hcl":
- b, err := json.Marshal(c)
- if err != nil {
- return ConfigMarshalError{err}
- }
- ast, err := hcl.Parse(string(b))
- if err != nil {
- return ConfigMarshalError{err}
- }
- err = printer.Fprint(f, ast.Node)
- if err != nil {
- return ConfigMarshalError{err}
- }
-
- case "prop", "props", "properties":
- if v.properties == nil {
- v.properties = properties.NewProperties()
- }
- p := v.properties
- for _, key := range v.AllKeys() {
- _, _, err := p.Set(key, v.GetString(key))
- if err != nil {
- return ConfigMarshalError{err}
- }
- }
- _, err := p.WriteComment(f, "#", properties.UTF8)
- if err != nil {
- return ConfigMarshalError{err}
- }
-
- case "dotenv", "env":
- lines := []string{}
- for _, key := range v.AllKeys() {
- envName := strings.ToUpper(strings.Replace(key, ".", "_", -1))
- val := v.Get(key)
- lines = append(lines, fmt.Sprintf("%v=%v", envName, val))
- }
- s := strings.Join(lines, "\n")
- if _, err := f.WriteString(s); err != nil {
- return ConfigMarshalError{err}
- }
-
- case "toml":
- t, err := toml.TreeFromMap(c)
- if err != nil {
- return ConfigMarshalError{err}
- }
- s := t.String()
- if _, err := f.WriteString(s); err != nil {
- return ConfigMarshalError{err}
- }
-
- case "yaml", "yml":
- b, err := yaml.Marshal(c)
+ _, err = f.WriteString(string(b))
if err != nil {
return ConfigMarshalError{err}
}
- if _, err = f.WriteString(string(b)); err != nil {
- return ConfigMarshalError{err}
- }
-
- case "ini":
- keys := v.AllKeys()
- cfg := ini.Empty()
- ini.PrettyFormat = false
- for i := 0; i < len(keys); i++ {
- key := keys[i]
- lastSep := strings.LastIndex(key, ".")
- sectionName := key[:(lastSep)]
- keyName := key[(lastSep + 1):]
- if sectionName == "default" {
- sectionName = ""
- }
- cfg.Section(sectionName).Key(keyName).SetValue(v.Get(key).(string))
- }
- cfg.WriteTo(f)
}
return nil
}
@@ -1650,7 +1729,8 @@ func keyExists(k string, m map[string]interface{}) string {
}
func castToMapStringInterface(
- src map[interface{}]interface{}) map[string]interface{} {
+ src map[interface{}]interface{},
+) map[string]interface{} {
tgt := map[string]interface{}{}
for k, v := range src {
tgt[fmt.Sprintf("%v", k)] = v
@@ -1658,6 +1738,14 @@ func castToMapStringInterface(
return tgt
}
+func castMapStringSliceToMapInterface(src map[string][]string) map[string]interface{} {
+ tgt := map[string]interface{}{}
+ for k, v := range src {
+ tgt[k] = v
+ }
+ return tgt
+}
+
func castMapStringToMapInterface(src map[string]string) map[string]interface{} {
tgt := map[string]interface{}{}
for k, v := range src {
@@ -1680,11 +1768,12 @@ func castMapFlagToMapInterface(src map[string]FlagValue) map[string]interface{}
// deep. Both map types are supported as there is a go-yaml fork that uses
// `map[string]interface{}` instead.
func mergeMaps(
- src, tgt map[string]interface{}, itgt map[interface{}]interface{}) {
+ src, tgt map[string]interface{}, itgt map[interface{}]interface{},
+) {
for sk, sv := range src {
tk := keyExists(sk, tgt)
if tk == "" {
- jww.TRACE.Printf("tk=\"\", tgt[%s]=%v", sk, sv)
+ v.logger.Trace("", "tk", "\"\"", fmt.Sprintf("tgt[%s]", sk), sv)
tgt[sk] = sv
if itgt != nil {
itgt[sk] = sv
@@ -1694,7 +1783,7 @@ func mergeMaps(
tv, ok := tgt[tk]
if !ok {
- jww.TRACE.Printf("tgt[%s] != ok, tgt[%s]=%v", tk, sk, sv)
+ v.logger.Trace("", fmt.Sprintf("ok[%s]", tk), false, fmt.Sprintf("tgt[%s]", sk), sv)
tgt[sk] = sv
if itgt != nil {
itgt[sk] = sv
@@ -1704,28 +1793,52 @@ func mergeMaps(
svType := reflect.TypeOf(sv)
tvType := reflect.TypeOf(tv)
- if svType != tvType {
- jww.ERROR.Printf(
- "svType != tvType; key=%s, st=%v, tt=%v, sv=%v, tv=%v",
- sk, svType, tvType, sv, tv)
- continue
- }
- jww.TRACE.Printf("processing key=%s, st=%v, tt=%v, sv=%v, tv=%v",
- sk, svType, tvType, sv, tv)
+ v.logger.Trace(
+ "processing",
+ "key", sk,
+ "st", svType,
+ "tt", tvType,
+ "sv", sv,
+ "tv", tv,
+ )
switch ttv := tv.(type) {
case map[interface{}]interface{}:
- jww.TRACE.Printf("merging maps (must convert)")
- tsv := sv.(map[interface{}]interface{})
+ v.logger.Trace("merging maps (must convert)")
+ tsv, ok := sv.(map[interface{}]interface{})
+ if !ok {
+ v.logger.Error(
+ "Could not cast sv to map[interface{}]interface{}",
+ "key", sk,
+ "st", svType,
+ "tt", tvType,
+ "sv", sv,
+ "tv", tv,
+ )
+ continue
+ }
+
ssv := castToMapStringInterface(tsv)
stv := castToMapStringInterface(ttv)
mergeMaps(ssv, stv, ttv)
case map[string]interface{}:
- jww.TRACE.Printf("merging maps")
- mergeMaps(sv.(map[string]interface{}), ttv, nil)
+ v.logger.Trace("merging maps")
+ tsv, ok := sv.(map[string]interface{})
+ if !ok {
+ v.logger.Error(
+ "Could not cast sv to map[string]interface{}",
+ "key", sk,
+ "st", svType,
+ "tt", tvType,
+ "sv", sv,
+ "tv", tv,
+ )
+ continue
+ }
+ mergeMaps(tsv, ttv, nil)
default:
- jww.TRACE.Printf("setting value")
+ v.logger.Trace("setting value")
tgt[tk] = sv
if itgt != nil {
itgt[tk] = sv
@@ -1737,6 +1850,7 @@ func mergeMaps(
// ReadRemoteConfig attempts to get configuration from a remote source
// and read it in the remote configuration registry.
func ReadRemoteConfig() error { return v.ReadRemoteConfig() }
+
func (v *Viper) ReadRemoteConfig() error {
return v.getKeyValueConfig()
}
@@ -1759,9 +1873,13 @@ func (v *Viper) getKeyValueConfig() error {
for _, rp := range v.remoteProviders {
val, err := v.getRemoteConfig(rp)
if err != nil {
+ v.logger.Error(fmt.Errorf("get remote config: %w", err).Error())
+
continue
}
+
v.kvstore = val
+
return nil
}
return RemoteConfigError("No Files Found")
@@ -1818,13 +1936,14 @@ func (v *Viper) watchRemoteConfig(provider RemoteProvider) (map[string]interface
// AllKeys returns all keys holding a value, regardless of where they are set.
// Nested keys are returned with a v.keyDelim separator
func AllKeys() []string { return v.AllKeys() }
+
func (v *Viper) AllKeys() []string {
m := map[string]bool{}
// add all paths, by order of descending priority to ensure correct shadowing
m = v.flattenAndMergeMap(m, castMapStringToMapInterface(v.aliases), "")
m = v.flattenAndMergeMap(m, v.override, "")
m = v.mergeFlatMap(m, castMapFlagToMapInterface(v.pflags))
- m = v.mergeFlatMap(m, castMapStringToMapInterface(v.env))
+ m = v.mergeFlatMap(m, castMapStringSliceToMapInterface(v.env))
m = v.flattenAndMergeMap(m, v.config, "")
m = v.flattenAndMergeMap(m, v.kvstore, "")
m = v.flattenAndMergeMap(m, v.defaults, "")
@@ -1898,6 +2017,7 @@ outer:
// AllSettings merges all settings and returns them as a map[string]interface{}.
func AllSettings() map[string]interface{} { return v.AllSettings() }
+
func (v *Viper) AllSettings() map[string]interface{} {
m := map[string]interface{}{}
// start from the list of keys, and construct the map one value at a time
@@ -1919,6 +2039,7 @@ func (v *Viper) AllSettings() map[string]interface{} {
// SetFs sets the filesystem to use to read configuration.
func SetFs(fs afero.Fs) { v.SetFs(fs) }
+
func (v *Viper) SetFs(fs afero.Fs) {
v.fs = fs
}
@@ -1926,6 +2047,7 @@ func (v *Viper) SetFs(fs afero.Fs) {
// SetConfigName sets name for the config file.
// Does not include extension.
func SetConfigName(in string) { v.SetConfigName(in) }
+
func (v *Viper) SetConfigName(in string) {
if in != "" {
v.configName = in
@@ -1936,6 +2058,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
@@ -1944,10 +2067,18 @@ func (v *Viper) SetConfigType(in string) {
// SetConfigPermissions sets the permissions for the config file.
func SetConfigPermissions(perm os.FileMode) { v.SetConfigPermissions(perm) }
+
func (v *Viper) SetConfigPermissions(perm os.FileMode) {
v.configPermissions = perm.Perm()
}
+// IniLoadOptions sets the load options for ini parsing.
+func IniLoadOptions(in ini.LoadOptions) Option {
+ return optionFunc(func(v *Viper) {
+ v.iniLoadOptions = in
+ })
+}
+
func (v *Viper) getConfigType() string {
if v.configType != "" {
return v.configType
@@ -1978,42 +2109,10 @@ func (v *Viper) getConfigFile() (string, error) {
return v.configFile, nil
}
-func (v *Viper) searchInPath(in string) (filename string) {
- jww.DEBUG.Println("Searching for config in ", in)
- for _, ext := range SupportedExts {
- jww.DEBUG.Println("Checking for", filepath.Join(in, v.configName+"."+ext))
- if b, _ := exists(v.fs, filepath.Join(in, v.configName+"."+ext)); b {
- jww.DEBUG.Println("Found: ", filepath.Join(in, v.configName+"."+ext))
- return filepath.Join(in, v.configName+"."+ext)
- }
- }
-
- if v.configType != "" {
- if b, _ := exists(v.fs, filepath.Join(in, v.configName)); b {
- return filepath.Join(in, v.configName)
- }
- }
-
- return ""
-}
-
-// Search all configPaths for any config file.
-// Returns the first path that exists (and is a config file).
-func (v *Viper) findConfigFile() (string, error) {
- jww.INFO.Println("Searching for config in ", v.configPaths)
-
- for _, cp := range v.configPaths {
- file := v.searchInPath(cp)
- if file != "" {
- return file, nil
- }
- }
- return "", ConfigFileNotFoundError{v.configName, fmt.Sprintf("%s", v.configPaths)}
-}
-
// Debug prints all configuration registries for debugging
// purposes.
func Debug() { v.Debug() }
+
func (v *Viper) Debug() {
fmt.Printf("Aliases:\n%#v\n", v.aliases)
fmt.Printf("Override:\n%#v\n", v.override)