spf13--viper/viper.go

1990 lines
58 KiB
Go
Raw Normal View History

2014-04-04 17:21:59 -04:00
// Copyright © 2014 Steve Francia <spf@spf13.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
2014-09-27 14:03:00 -07:00
// Viper is a application configuration system.
// It believes that applications can be configured a variety of ways
2014-10-24 15:38:01 -04:00
// via flags, ENVIRONMENT variables, configuration files retrieved
// from the file system, or a remote key/value store.
2014-09-27 14:03:00 -07:00
// Each item takes precedence over the item below it:
// overrides
2014-09-27 14:03:00 -07:00
// flag
// env
// config
2014-10-24 15:38:01 -04:00
// key/value store
2014-09-27 14:03:00 -07:00
// default
2014-04-04 17:21:59 -04:00
package viper
import (
"bytes"
2017-04-17 10:08:15 +02:00
"encoding/csv"
"encoding/json"
2014-04-04 17:21:59 -04:00
"fmt"
"io"
"log"
2014-04-04 17:21:59 -04:00
"os"
"path/filepath"
2014-10-24 15:38:01 -04:00
"reflect"
2014-04-05 01:19:39 -04:00
"strings"
"sync"
2014-04-04 17:21:59 -04:00
"time"
"github.com/fsnotify/fsnotify"
"github.com/mitchellh/mapstructure"
2016-08-05 00:24:49 -07:00
"github.com/spf13/afero"
2015-07-30 15:44:12 -05:00
"github.com/spf13/cast"
"github.com/spf13/pflag"
2014-04-04 17:21:59 -04:00
)
// ConfigMarshalError happens when failing to marshal the configuration.
type ConfigMarshalError struct {
err error
}
// Error returns the formatted configuration error.
func (e ConfigMarshalError) Error() string {
return fmt.Sprintf("While marshaling config: %s", e.err.Error())
}
var v *Viper
func init() {
v = New()
}
// UnsupportedConfigError denotes encountering an unsupported
// configuration filetype.
type UnsupportedConfigError string
// Error returns the formatted configuration error.
func (str UnsupportedConfigError) Error() string {
return fmt.Sprintf("Unsupported Config Type %q", string(str))
}
// ConfigFileNotFoundError denotes failing to find configuration file.
2015-08-01 19:37:27 -05:00
type ConfigFileNotFoundError struct {
name, locations string
}
// Error returns the formatted configuration error.
2015-08-01 19:37:27 -05:00
func (fnfe ConfigFileNotFoundError) Error() string {
return fmt.Sprintf("Config File %q Not Found in %q", fnfe.name, fnfe.locations)
}
2020-09-08 10:34:59 +08:00
// ConfigFileAlreadyExistsError denotes failure to write new configuration file.
type ConfigFileAlreadyExistsError string
// Error returns the formatted error when configuration already exists.
func (faee ConfigFileAlreadyExistsError) Error() string {
return fmt.Sprintf("Config File %q Already Exists", string(faee))
}
// A DecoderConfigOption can be passed to viper.Unmarshal to configure
// mapstructure.DecoderConfig options
type DecoderConfigOption func(*mapstructure.DecoderConfig)
// DecodeHook returns a DecoderConfigOption which overrides the default
// DecoderConfig.DecodeHook value, the default is:
//
2022-09-16 21:35:05 +08:00
// mapstructure.ComposeDecodeHookFunc(
// mapstructure.StringToTimeDurationHookFunc(),
// mapstructure.StringToSliceHookFunc(","),
// )
func DecodeHook(hook mapstructure.DecodeHookFunc) DecoderConfigOption {
return func(c *mapstructure.DecoderConfig) {
c.DecodeHook = hook
}
}
// Viper is a prioritized configuration registry. It
// maintains a set of configuration sources, fetches
// values to populate those, and provides them according
// to the source's priority.
// The priority of the sources is the following:
// 1. overrides
// 2. flags
// 3. env. variables
// 4. config file
// 5. key/value store
// 6. defaults
//
// For example, if values from the following sources were loaded:
//
2022-09-16 21:35:05 +08:00
// Defaults : {
// "secret": "",
// "user": "default",
// "endpoint": "https://localhost"
// }
// Config : {
// "user": "root"
// "secret": "defaultsecret"
// }
// Env : {
// "secret": "somesecretkey"
// }
//
// The resulting config will have the following values:
//
// {
// "secret": "somesecretkey",
// "user": "root",
// "endpoint": "https://localhost"
// }
type Viper struct {
// Delimiter that separates a list of keys
// used to access a nested value in one go
keyDelim string
// A set of paths to look for the config file in
configPaths []string
// The filesystem to read config from.
fs afero.Fs
// Name of file to look for inside the path
2020-09-08 10:34:59 +08:00
configName string
configFile string
configPermissions os.FileMode
envPrefix string
logger Logger
automaticEnvApplied bool
2020-09-08 10:34:59 +08:00
envKeyReplacer StringReplacer
allowEmptyEnv bool
[110] Default Values Specify Type This patch adds a feature, if enabled, will infer a value's type from its default value no matter from where else the value is set. This is particularly important when working with environment variables. For example: package main import ( "fmt" "os" "github.com/spf13/viper" ) func print(name string, val interface{}) { fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val) } func main() { viper.BindEnv("mykey", "MYPREFIX_MYKEY") viper.SetDefault("mykey", []string{}) os.Setenv("MYPREFIX_MYKEY", "a b c") v1 := viper.GetStringSlice("mykey") v2 := viper.Get("mykey") print("v1", v1) print("v2", v2) } When this program is executed the following is emitted: [0]akutz@pax:ex$ ./ex1 v1 []string [a b c] v2 string a b c [0]akutz@pax:ex$ You may wonder, why is this important? Just use the GetStringSlice function. Well, it *becomes* important when dealing with marshaling. If we update the above program to this: package main import ( "fmt" "os" "github.com/spf13/viper" ) type Data struct { MyKey []string } func print(name string, val interface{}) { fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val) } func main() { viper.BindEnv("mykey", "MYPREFIX_MYKEY") viper.SetDefault("mykey", []string{}) os.Setenv("MYPREFIX_MYKEY", "a b c") v1 := viper.GetStringSlice("mykey") v2 := viper.Get("mykey") print("v1", v1) print("v2", v2) d := &Data{} viper.Marshal(d) print("d.MyKey", d.MyKey) } Now we can see the issue when we execute the updated program: [0]akutz@pax:ex$ ./ex2 v1 []string [a b c] v2 string a b c d.MyKey []string [] [0]akutz@pax:ex$ The marshalled data structure's field MyKey is empty when in fact it should have a string slice equal to, in value, []string {"a", "b", "c"}. The problem is that viper's Marshal function calls AllSettings which ultimately uses the Get function. The Get function does try to infer the value's type, but it does so using the type of the value retrieved using this logic: Get has the behavior of returning the value associated with the first place from where it is set. Viper will check in the following order: * override * flag * env * config file * key/value store * default While the above order is the one we want when retrieving the values, this patch enables users to decide if it's the order they want to be used when inferring a value's type. To that end the function SetTypeByDefaultValue is introduced. When SetTypeByDefaultValue(true) is called, a call to the Get function will now first check a key's default value, if set, when inferring a value's type. This is demonstrated using a modified version of the same program above: package main import ( "fmt" "os" "github.com/spf13/viper" ) type Data struct { MyKey []string } func print(name string, val interface{}) { fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val) } func main() { viper.BindEnv("mykey", "MYPREFIX_MYKEY") viper.SetDefault("mykey", []string{}) os.Setenv("MYPREFIX_MYKEY", "a b c") v1 := viper.GetStringSlice("mykey") v2 := viper.Get("mykey") print("v1", v1) print("v2", v2) d1 := &Data{} viper.Marshal(d1) print("d1.MyKey", d1.MyKey) viper.SetTypeByDefaultValue(true) d2 := &Data{} viper.Marshal(d2) print("d2.MyKey", d2.MyKey) } Now the following is emitted: [0]akutz@pax:ex$ ./ex3 v1 []string [a b c] v2 string a b c d1.MyKey []string [] d2.MyKey []string [a b c] [0]akutz@pax:ex$
2015-08-29 10:54:20 -05:00
config map[string]interface{}
override map[string]interface{}
defaults map[string]interface{}
kvstore map[string]interface{}
pflags map[string]FlagValue
[110] Default Values Specify Type This patch adds a feature, if enabled, will infer a value's type from its default value no matter from where else the value is set. This is particularly important when working with environment variables. For example: package main import ( "fmt" "os" "github.com/spf13/viper" ) func print(name string, val interface{}) { fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val) } func main() { viper.BindEnv("mykey", "MYPREFIX_MYKEY") viper.SetDefault("mykey", []string{}) os.Setenv("MYPREFIX_MYKEY", "a b c") v1 := viper.GetStringSlice("mykey") v2 := viper.Get("mykey") print("v1", v1) print("v2", v2) } When this program is executed the following is emitted: [0]akutz@pax:ex$ ./ex1 v1 []string [a b c] v2 string a b c [0]akutz@pax:ex$ You may wonder, why is this important? Just use the GetStringSlice function. Well, it *becomes* important when dealing with marshaling. If we update the above program to this: package main import ( "fmt" "os" "github.com/spf13/viper" ) type Data struct { MyKey []string } func print(name string, val interface{}) { fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val) } func main() { viper.BindEnv("mykey", "MYPREFIX_MYKEY") viper.SetDefault("mykey", []string{}) os.Setenv("MYPREFIX_MYKEY", "a b c") v1 := viper.GetStringSlice("mykey") v2 := viper.Get("mykey") print("v1", v1) print("v2", v2) d := &Data{} viper.Marshal(d) print("d.MyKey", d.MyKey) } Now we can see the issue when we execute the updated program: [0]akutz@pax:ex$ ./ex2 v1 []string [a b c] v2 string a b c d.MyKey []string [] [0]akutz@pax:ex$ The marshalled data structure's field MyKey is empty when in fact it should have a string slice equal to, in value, []string {"a", "b", "c"}. The problem is that viper's Marshal function calls AllSettings which ultimately uses the Get function. The Get function does try to infer the value's type, but it does so using the type of the value retrieved using this logic: Get has the behavior of returning the value associated with the first place from where it is set. Viper will check in the following order: * override * flag * env * config file * key/value store * default While the above order is the one we want when retrieving the values, this patch enables users to decide if it's the order they want to be used when inferring a value's type. To that end the function SetTypeByDefaultValue is introduced. When SetTypeByDefaultValue(true) is called, a call to the Get function will now first check a key's default value, if set, when inferring a value's type. This is demonstrated using a modified version of the same program above: package main import ( "fmt" "os" "github.com/spf13/viper" ) type Data struct { MyKey []string } func print(name string, val interface{}) { fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val) } func main() { viper.BindEnv("mykey", "MYPREFIX_MYKEY") viper.SetDefault("mykey", []string{}) os.Setenv("MYPREFIX_MYKEY", "a b c") v1 := viper.GetStringSlice("mykey") v2 := viper.Get("mykey") print("v1", v1) print("v2", v2) d1 := &Data{} viper.Marshal(d1) print("d1.MyKey", d1.MyKey) viper.SetTypeByDefaultValue(true) d2 := &Data{} viper.Marshal(d2) print("d2.MyKey", d2.MyKey) } Now the following is emitted: [0]akutz@pax:ex$ ./ex3 v1 []string [a b c] v2 string a b c d1.MyKey []string [] d2.MyKey []string [a b c] [0]akutz@pax:ex$
2015-08-29 10:54:20 -05:00
env map[string]string
aliases map[string]string
typeByDefValue bool
onConfigChange func(fsnotify.Event)
}
// New returns an initialized Viper instance.
func New() *Viper {
v := new(Viper)
v.keyDelim = "."
v.configName = "config"
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.aliases = make(map[string]string)
2020-09-08 09:20:53 +08:00
v.logger = DefaultLogger(INFO)
[110] Default Values Specify Type This patch adds a feature, if enabled, will infer a value's type from its default value no matter from where else the value is set. This is particularly important when working with environment variables. For example: package main import ( "fmt" "os" "github.com/spf13/viper" ) func print(name string, val interface{}) { fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val) } func main() { viper.BindEnv("mykey", "MYPREFIX_MYKEY") viper.SetDefault("mykey", []string{}) os.Setenv("MYPREFIX_MYKEY", "a b c") v1 := viper.GetStringSlice("mykey") v2 := viper.Get("mykey") print("v1", v1) print("v2", v2) } When this program is executed the following is emitted: [0]akutz@pax:ex$ ./ex1 v1 []string [a b c] v2 string a b c [0]akutz@pax:ex$ You may wonder, why is this important? Just use the GetStringSlice function. Well, it *becomes* important when dealing with marshaling. If we update the above program to this: package main import ( "fmt" "os" "github.com/spf13/viper" ) type Data struct { MyKey []string } func print(name string, val interface{}) { fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val) } func main() { viper.BindEnv("mykey", "MYPREFIX_MYKEY") viper.SetDefault("mykey", []string{}) os.Setenv("MYPREFIX_MYKEY", "a b c") v1 := viper.GetStringSlice("mykey") v2 := viper.Get("mykey") print("v1", v1) print("v2", v2) d := &Data{} viper.Marshal(d) print("d.MyKey", d.MyKey) } Now we can see the issue when we execute the updated program: [0]akutz@pax:ex$ ./ex2 v1 []string [a b c] v2 string a b c d.MyKey []string [] [0]akutz@pax:ex$ The marshalled data structure's field MyKey is empty when in fact it should have a string slice equal to, in value, []string {"a", "b", "c"}. The problem is that viper's Marshal function calls AllSettings which ultimately uses the Get function. The Get function does try to infer the value's type, but it does so using the type of the value retrieved using this logic: Get has the behavior of returning the value associated with the first place from where it is set. Viper will check in the following order: * override * flag * env * config file * key/value store * default While the above order is the one we want when retrieving the values, this patch enables users to decide if it's the order they want to be used when inferring a value's type. To that end the function SetTypeByDefaultValue is introduced. When SetTypeByDefaultValue(true) is called, a call to the Get function will now first check a key's default value, if set, when inferring a value's type. This is demonstrated using a modified version of the same program above: package main import ( "fmt" "os" "github.com/spf13/viper" ) type Data struct { MyKey []string } func print(name string, val interface{}) { fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val) } func main() { viper.BindEnv("mykey", "MYPREFIX_MYKEY") viper.SetDefault("mykey", []string{}) os.Setenv("MYPREFIX_MYKEY", "a b c") v1 := viper.GetStringSlice("mykey") v2 := viper.Get("mykey") print("v1", v1) print("v2", v2) d1 := &Data{} viper.Marshal(d1) print("d1.MyKey", d1.MyKey) viper.SetTypeByDefaultValue(true) d2 := &Data{} viper.Marshal(d2) print("d2.MyKey", d2.MyKey) } Now the following is emitted: [0]akutz@pax:ex$ ./ex3 v1 []string [a b c] v2 string a b c d1.MyKey []string [] d2.MyKey []string [a b c] [0]akutz@pax:ex$
2015-08-29 10:54:20 -05:00
v.typeByDefValue = false
return v
}
2020-09-08 10:34:59 +08:00
// Option configures Viper using the functional options paradigm popularized by Rob Pike and Dave Cheney.
// If you're unfamiliar with this style,
// see https://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html and
// https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis.
type Option interface {
apply(v *Viper)
}
type optionFunc func(v *Viper)
func (fn optionFunc) apply(v *Viper) {
fn(v)
}
// KeyDelimiter sets the delimiter used for determining key parts.
// By default it's value is ".".
func KeyDelimiter(d string) Option {
return optionFunc(func(v *Viper) {
v.keyDelim = d
})
}
// StringReplacer applies a set of replacements to a string.
type StringReplacer interface {
// Replace returns a copy of s with all replacements performed.
Replace(s string) string
}
// EnvKeyReplacer sets a replacer used for mapping environment variables to internal keys.
func EnvKeyReplacer(r StringReplacer) Option {
return optionFunc(func(v *Viper) {
v.envKeyReplacer = r
})
}
// NewWithOptions creates a new Viper instance.
func NewWithOptions(opts ...Option) *Viper {
v := New()
for _, opt := range opts {
opt.apply(v)
}
return v
}
2020-09-08 09:20:53 +08:00
// Reset Intended for testing, will reset all to default settings.
// In the public interface for the viper package so applications
// can use it in their testing as well.
func Reset() {
v = New()
SupportedExts = []string{"json"}
2015-05-30 21:28:33 +02:00
}
// SupportedExts are universally supported extensions.
var SupportedExts = []string{"json"}
2014-04-04 17:21:59 -04:00
2020-09-08 09:20:53 +08:00
// OnConfigChange are check change event
func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) }
2020-09-08 09:20:53 +08:00
// OnConfigChange are check change event
func (v *Viper) OnConfigChange(run func(in fsnotify.Event)) {
v.onConfigChange = run
}
2020-09-08 09:20:53 +08:00
// WatchConfig watch config change
func WatchConfig() { v.WatchConfig() }
2020-09-08 09:20:53 +08:00
// WatchConfig watch config change
func (v *Viper) WatchConfig() {
initWG := sync.WaitGroup{}
initWG.Add(1)
go func() {
watcher, err := fsnotify.NewWatcher()
if err != nil {
2020-09-08 09:20:53 +08:00
v.logger.Errorf("fsnotify watcher err: %v", err)
}
defer watcher.Close()
// we have to watch the entire directory to pick up renames/atomic saves in a cross-platform way
filename, err := v.getConfigFile()
if err != nil {
2020-09-08 09:20:53 +08:00
v.logger.Errorf("getConfigFile error: %v\n", err)
2020-09-08 10:34:59 +08:00
initWG.Done()
return
}
configFile := filepath.Clean(filename)
configDir, _ := filepath.Split(configFile)
realConfigFile, _ := filepath.EvalSymlinks(filename)
eventsWG := sync.WaitGroup{}
eventsWG.Add(1)
go func() {
for {
select {
case event, ok := <-watcher.Events:
if !ok { // 'Events' channel is closed
eventsWG.Done()
return
}
currentConfigFile, _ := filepath.EvalSymlinks(filename)
// we only care about the config file with the following cases:
// 1 - if the config file was modified or created
// 2 - if the real path to the config file changed (eg: k8s ConfigMap replacement)
const writeOrCreateMask = fsnotify.Write | fsnotify.Create
if (filepath.Clean(event.Name) == configFile &&
event.Op&writeOrCreateMask != 0) ||
(currentConfigFile != "" && currentConfigFile != realConfigFile) {
realConfigFile = currentConfigFile
err := v.ReadInConfig()
if err != nil {
2020-09-08 09:20:53 +08:00
v.logger.Errorf("error reading config file: %v\n", err)
}
if v.onConfigChange != nil {
v.onConfigChange(event)
}
} else if filepath.Clean(event.Name) == configFile &&
event.Op&fsnotify.Remove&fsnotify.Remove != 0 {
eventsWG.Done()
return
}
case err, ok := <-watcher.Errors:
if ok { // 'Errors' channel is not closed
2020-09-08 09:20:53 +08:00
v.logger.Errorf("watcher error: %v\n", err)
}
eventsWG.Done()
return
}
}
}()
watcher.Add(configDir)
initWG.Done() // done initalizing the watch in this go routine, so the parent routine can move on...
eventsWG.Wait() // now, wait for event loop to end in this go-routine...
}()
initWG.Wait() // make sure that the go routine above fully ended before returning
}
// 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) }
2020-09-08 09:20:53 +08:00
// SetConfigFile explicitly defines the path, name and extension of the config file.
// Viper will use this and not check any of the config paths.
func (v *Viper) SetConfigFile(in string) {
2014-04-04 17:21:59 -04:00
if in != "" {
v.configFile = in
2014-04-04 17:21:59 -04:00
}
}
// SetEnvPrefix defines a prefix that ENVIRONMENT variables will use.
// E.g. if your prefix is "spf", the env registry will look for env
// variables that start with "SPF_".
func SetEnvPrefix(in string) { v.SetEnvPrefix(in) }
2020-09-08 09:20:53 +08:00
// SetEnvPrefix defines a prefix that ENVIRONMENT variables will use.
// E.g. if your prefix is "spf", the env registry will look for env
// variables that start with "SPF_".
func (v *Viper) SetEnvPrefix(in string) {
if in != "" {
v.envPrefix = in
}
}
func (v *Viper) mergeWithEnvPrefix(in string) string {
if v.envPrefix != "" {
return strings.ToUpper(v.envPrefix + "_" + in)
}
return strings.ToUpper(in)
}
2020-09-08 10:34:59 +08:00
// AllowEmptyEnv tells Viper to consider set,
// 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) }
// AllowEmptyEnv tells Viper to consider set,
// but empty environment variables as valid values instead of falling back.
// For backward compatibility reasons this is false by default.
func (v *Viper) AllowEmptyEnv(allowEmptyEnv bool) {
v.allowEmptyEnv = allowEmptyEnv
}
2015-03-06 11:21:17 -08:00
// TODO: should getEnv logic be moved into find(). Can generalize the use of
// rewriting keys many things, Ex: Get('someKey') -> some_key
// (camel case to snake case for JSON keys perhaps)
2015-03-06 11:21:17 -08:00
// getEnv is a wrapper around os.Getenv which replaces characters in the original
// key. This allows env vars which have different keys than the config object
// keys.
2020-09-08 10:34:59 +08:00
func (v *Viper) getEnv(key string) (string, bool) {
2015-03-06 11:21:17 -08:00
if v.envKeyReplacer != nil {
key = v.envKeyReplacer.Replace(key)
}
2020-09-08 10:34:59 +08:00
val, ok := os.LookupEnv(key)
return val, ok && (v.allowEmptyEnv || val != "")
2015-03-06 11:21:17 -08:00
}
2020-09-08 09:20:53 +08:00
// WithLogger returns a new Options value with Logger set to the given value.
//
// Logger provides a way to configure what logger each value of badger.DB uses.
//
// The default value of Logger writes to stderr using the log package from the Go standard library.
func (v *Viper) WithLogger(val Logger) {
v.logger = val
}
// WithLoggingLevel returns a new Options value with logging level of the
// default logger set to the given value.
// LoggingLevel sets the level of logging. It should be one of DEBUG, INFO,
// WARNING or ERROR levels.
//
// The default value of LoggingLevel is INFO.
func (v *Viper) WithLoggingLevel(val loggingLevel) {
v.logger = DefaultLogger(val)
}
// ConfigFileUsed returns the file used to populate the config registry.
func ConfigFileUsed() string { return v.ConfigFileUsed() }
// ConfigFileUsed returns the file used to populate the config registry.
func (v *Viper) ConfigFileUsed() string { return v.configFile }
2014-04-07 23:35:40 -04:00
// AddConfigPath adds a path for Viper to search for the config file in.
2014-07-11 10:42:07 -04:00
// Can be called multiple times to define multiple search paths.
func AddConfigPath(in string) { v.AddConfigPath(in) }
2020-09-08 09:20:53 +08:00
// AddConfigPath adds a path for Viper to search for the config file in.
// Can be called multiple times to define multiple search paths.
func (v *Viper) AddConfigPath(in string) {
2014-04-04 17:21:59 -04:00
if in != "" {
absin := absPathify(in)
2020-09-08 09:20:53 +08:00
v.logger.Infof("adding %s to paths to search", absin)
if !stringInSlice(absin, v.configPaths) {
v.configPaths = append(v.configPaths, absin)
2014-04-04 17:21:59 -04:00
}
}
}
// searchMap recursively searches for a value for path in source map.
// Returns nil if not found.
// Note: This assumes that the path entries and map keys are lower cased.
func (v *Viper) searchMap(source map[string]interface{}, path []string) interface{} {
if len(path) == 0 {
return source
}
next, ok := source[path[0]]
if ok {
// Fast path
if len(path) == 1 {
return next
}
// Nested case
switch next.(type) {
case map[interface{}]interface{}:
return v.searchMap(cast.ToStringMap(next), path[1:])
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
return v.searchMap(next.(map[string]interface{}), path[1:])
default:
// got a value but nested key expected, return "nil" for not found
return nil
}
}
return nil
}
// searchMapWithPathPrefixes recursively searches for a value for path in source map.
//
// While searchMap() considers each path element as a single map key, 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"].
//
// This should be useful only at config level (other maps may not contain dots
// 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{} {
if len(path) == 0 {
return source
}
// search for path prefixes, starting from the longest one
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
}
}
}
// 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”
2022-09-16 21:35:05 +08:00
//
// "foo.bar.baz" in a lower-priority map
func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]interface{}) string {
var parentVal interface{}
for i := 1; i < len(path); i++ {
parentVal = v.searchMap(m, path[0:i])
if parentVal == nil {
// not found, no need to add more path elements
return ""
}
switch parentVal.(type) {
case map[interface{}]interface{}:
continue
case map[string]interface{}:
continue
default:
// parentVal is a regular value which shadows "path"
return strings.Join(path[0:i], v.keyDelim)
}
}
return ""
}
// isPathShadowedInFlatMap makes sure the given path is not shadowed somewhere
// in a sub-path of the map.
// e.g., if "foo.bar" has a value in the given map, it “shadows”
2022-09-16 21:35:05 +08:00
//
// "foo.bar.baz" in a lower-priority map
func (v *Viper) isPathShadowedInFlatMap(path []string, mi interface{}) string {
// unify input map
var m map[string]interface{}
switch mi.(type) {
case map[string]string, map[string]FlagValue:
m = cast.ToStringMap(mi)
default:
return ""
}
// scan paths
var parentKey string
for i := 1; i < len(path); i++ {
parentKey = strings.Join(path[0:i], v.keyDelim)
if _, ok := m[parentKey]; ok {
return parentKey
}
}
return ""
}
// isPathShadowedInAutoEnv makes sure the given path is not shadowed somewhere
// in the environment, when automatic env is on.
// e.g., if "foo.bar" has a value in the environment, it “shadows”
2022-09-16 21:35:05 +08:00
//
// "foo.bar.baz" in a lower-priority map
func (v *Viper) isPathShadowedInAutoEnv(path []string) string {
var parentKey string
for i := 1; i < len(path); i++ {
parentKey = strings.Join(path[0:i], v.keyDelim)
2020-09-08 10:34:59 +08:00
if _, ok := v.getEnv(v.mergeWithEnvPrefix(parentKey)); ok {
return parentKey
}
}
return ""
}
[110] Default Values Specify Type This patch adds a feature, if enabled, will infer a value's type from its default value no matter from where else the value is set. This is particularly important when working with environment variables. For example: package main import ( "fmt" "os" "github.com/spf13/viper" ) func print(name string, val interface{}) { fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val) } func main() { viper.BindEnv("mykey", "MYPREFIX_MYKEY") viper.SetDefault("mykey", []string{}) os.Setenv("MYPREFIX_MYKEY", "a b c") v1 := viper.GetStringSlice("mykey") v2 := viper.Get("mykey") print("v1", v1) print("v2", v2) } When this program is executed the following is emitted: [0]akutz@pax:ex$ ./ex1 v1 []string [a b c] v2 string a b c [0]akutz@pax:ex$ You may wonder, why is this important? Just use the GetStringSlice function. Well, it *becomes* important when dealing with marshaling. If we update the above program to this: package main import ( "fmt" "os" "github.com/spf13/viper" ) type Data struct { MyKey []string } func print(name string, val interface{}) { fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val) } func main() { viper.BindEnv("mykey", "MYPREFIX_MYKEY") viper.SetDefault("mykey", []string{}) os.Setenv("MYPREFIX_MYKEY", "a b c") v1 := viper.GetStringSlice("mykey") v2 := viper.Get("mykey") print("v1", v1) print("v2", v2) d := &Data{} viper.Marshal(d) print("d.MyKey", d.MyKey) } Now we can see the issue when we execute the updated program: [0]akutz@pax:ex$ ./ex2 v1 []string [a b c] v2 string a b c d.MyKey []string [] [0]akutz@pax:ex$ The marshalled data structure's field MyKey is empty when in fact it should have a string slice equal to, in value, []string {"a", "b", "c"}. The problem is that viper's Marshal function calls AllSettings which ultimately uses the Get function. The Get function does try to infer the value's type, but it does so using the type of the value retrieved using this logic: Get has the behavior of returning the value associated with the first place from where it is set. Viper will check in the following order: * override * flag * env * config file * key/value store * default While the above order is the one we want when retrieving the values, this patch enables users to decide if it's the order they want to be used when inferring a value's type. To that end the function SetTypeByDefaultValue is introduced. When SetTypeByDefaultValue(true) is called, a call to the Get function will now first check a key's default value, if set, when inferring a value's type. This is demonstrated using a modified version of the same program above: package main import ( "fmt" "os" "github.com/spf13/viper" ) type Data struct { MyKey []string } func print(name string, val interface{}) { fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val) } func main() { viper.BindEnv("mykey", "MYPREFIX_MYKEY") viper.SetDefault("mykey", []string{}) os.Setenv("MYPREFIX_MYKEY", "a b c") v1 := viper.GetStringSlice("mykey") v2 := viper.Get("mykey") print("v1", v1) print("v2", v2) d1 := &Data{} viper.Marshal(d1) print("d1.MyKey", d1.MyKey) viper.SetTypeByDefaultValue(true) d2 := &Data{} viper.Marshal(d2) print("d2.MyKey", d2.MyKey) } Now the following is emitted: [0]akutz@pax:ex$ ./ex3 v1 []string [a b c] v2 string a b c d1.MyKey []string [] d2.MyKey []string [a b c] [0]akutz@pax:ex$
2015-08-29 10:54:20 -05:00
// SetTypeByDefaultValue enables or disables the inference of a key value's
// type when the Get function is used based upon a key's default value as
// opposed to the value returned based on the normal fetch logic.
//
// For example, if a key has a default value of []string{} and the same key
// is set via an environment variable to "a b c", a call to the Get function
// would return a string slice for the key if the key's type is inferred by
// the default value and the Get function would return:
//
2022-09-16 21:35:05 +08:00
// []string {"a", "b", "c"}
[110] Default Values Specify Type This patch adds a feature, if enabled, will infer a value's type from its default value no matter from where else the value is set. This is particularly important when working with environment variables. For example: package main import ( "fmt" "os" "github.com/spf13/viper" ) func print(name string, val interface{}) { fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val) } func main() { viper.BindEnv("mykey", "MYPREFIX_MYKEY") viper.SetDefault("mykey", []string{}) os.Setenv("MYPREFIX_MYKEY", "a b c") v1 := viper.GetStringSlice("mykey") v2 := viper.Get("mykey") print("v1", v1) print("v2", v2) } When this program is executed the following is emitted: [0]akutz@pax:ex$ ./ex1 v1 []string [a b c] v2 string a b c [0]akutz@pax:ex$ You may wonder, why is this important? Just use the GetStringSlice function. Well, it *becomes* important when dealing with marshaling. If we update the above program to this: package main import ( "fmt" "os" "github.com/spf13/viper" ) type Data struct { MyKey []string } func print(name string, val interface{}) { fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val) } func main() { viper.BindEnv("mykey", "MYPREFIX_MYKEY") viper.SetDefault("mykey", []string{}) os.Setenv("MYPREFIX_MYKEY", "a b c") v1 := viper.GetStringSlice("mykey") v2 := viper.Get("mykey") print("v1", v1) print("v2", v2) d := &Data{} viper.Marshal(d) print("d.MyKey", d.MyKey) } Now we can see the issue when we execute the updated program: [0]akutz@pax:ex$ ./ex2 v1 []string [a b c] v2 string a b c d.MyKey []string [] [0]akutz@pax:ex$ The marshalled data structure's field MyKey is empty when in fact it should have a string slice equal to, in value, []string {"a", "b", "c"}. The problem is that viper's Marshal function calls AllSettings which ultimately uses the Get function. The Get function does try to infer the value's type, but it does so using the type of the value retrieved using this logic: Get has the behavior of returning the value associated with the first place from where it is set. Viper will check in the following order: * override * flag * env * config file * key/value store * default While the above order is the one we want when retrieving the values, this patch enables users to decide if it's the order they want to be used when inferring a value's type. To that end the function SetTypeByDefaultValue is introduced. When SetTypeByDefaultValue(true) is called, a call to the Get function will now first check a key's default value, if set, when inferring a value's type. This is demonstrated using a modified version of the same program above: package main import ( "fmt" "os" "github.com/spf13/viper" ) type Data struct { MyKey []string } func print(name string, val interface{}) { fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val) } func main() { viper.BindEnv("mykey", "MYPREFIX_MYKEY") viper.SetDefault("mykey", []string{}) os.Setenv("MYPREFIX_MYKEY", "a b c") v1 := viper.GetStringSlice("mykey") v2 := viper.Get("mykey") print("v1", v1) print("v2", v2) d1 := &Data{} viper.Marshal(d1) print("d1.MyKey", d1.MyKey) viper.SetTypeByDefaultValue(true) d2 := &Data{} viper.Marshal(d2) print("d2.MyKey", d2.MyKey) } Now the following is emitted: [0]akutz@pax:ex$ ./ex3 v1 []string [a b c] v2 string a b c d1.MyKey []string [] d2.MyKey []string [a b c] [0]akutz@pax:ex$
2015-08-29 10:54:20 -05:00
//
// Otherwise the Get function would return:
//
2022-09-16 21:35:05 +08:00
// "a b c"
[110] Default Values Specify Type This patch adds a feature, if enabled, will infer a value's type from its default value no matter from where else the value is set. This is particularly important when working with environment variables. For example: package main import ( "fmt" "os" "github.com/spf13/viper" ) func print(name string, val interface{}) { fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val) } func main() { viper.BindEnv("mykey", "MYPREFIX_MYKEY") viper.SetDefault("mykey", []string{}) os.Setenv("MYPREFIX_MYKEY", "a b c") v1 := viper.GetStringSlice("mykey") v2 := viper.Get("mykey") print("v1", v1) print("v2", v2) } When this program is executed the following is emitted: [0]akutz@pax:ex$ ./ex1 v1 []string [a b c] v2 string a b c [0]akutz@pax:ex$ You may wonder, why is this important? Just use the GetStringSlice function. Well, it *becomes* important when dealing with marshaling. If we update the above program to this: package main import ( "fmt" "os" "github.com/spf13/viper" ) type Data struct { MyKey []string } func print(name string, val interface{}) { fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val) } func main() { viper.BindEnv("mykey", "MYPREFIX_MYKEY") viper.SetDefault("mykey", []string{}) os.Setenv("MYPREFIX_MYKEY", "a b c") v1 := viper.GetStringSlice("mykey") v2 := viper.Get("mykey") print("v1", v1) print("v2", v2) d := &Data{} viper.Marshal(d) print("d.MyKey", d.MyKey) } Now we can see the issue when we execute the updated program: [0]akutz@pax:ex$ ./ex2 v1 []string [a b c] v2 string a b c d.MyKey []string [] [0]akutz@pax:ex$ The marshalled data structure's field MyKey is empty when in fact it should have a string slice equal to, in value, []string {"a", "b", "c"}. The problem is that viper's Marshal function calls AllSettings which ultimately uses the Get function. The Get function does try to infer the value's type, but it does so using the type of the value retrieved using this logic: Get has the behavior of returning the value associated with the first place from where it is set. Viper will check in the following order: * override * flag * env * config file * key/value store * default While the above order is the one we want when retrieving the values, this patch enables users to decide if it's the order they want to be used when inferring a value's type. To that end the function SetTypeByDefaultValue is introduced. When SetTypeByDefaultValue(true) is called, a call to the Get function will now first check a key's default value, if set, when inferring a value's type. This is demonstrated using a modified version of the same program above: package main import ( "fmt" "os" "github.com/spf13/viper" ) type Data struct { MyKey []string } func print(name string, val interface{}) { fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val) } func main() { viper.BindEnv("mykey", "MYPREFIX_MYKEY") viper.SetDefault("mykey", []string{}) os.Setenv("MYPREFIX_MYKEY", "a b c") v1 := viper.GetStringSlice("mykey") v2 := viper.Get("mykey") print("v1", v1) print("v2", v2) d1 := &Data{} viper.Marshal(d1) print("d1.MyKey", d1.MyKey) viper.SetTypeByDefaultValue(true) d2 := &Data{} viper.Marshal(d2) print("d2.MyKey", d2.MyKey) } Now the following is emitted: [0]akutz@pax:ex$ ./ex3 v1 []string [a b c] v2 string a b c d1.MyKey []string [] d2.MyKey []string [a b c] [0]akutz@pax:ex$
2015-08-29 10:54:20 -05:00
func SetTypeByDefaultValue(enable bool) { v.SetTypeByDefaultValue(enable) }
2020-09-08 09:20:53 +08:00
// SetTypeByDefaultValue enables or disables the inference of a key value's
// type when the Get function is used based upon a key's default value as
// opposed to the value returned based on the normal fetch logic.
//
// For example, if a key has a default value of []string{} and the same key
// is set via an environment variable to "a b c", a call to the Get function
// would return a string slice for the key if the key's type is inferred by
// the default value and the Get function would return:
//
2022-09-16 21:35:05 +08:00
// []string {"a", "b", "c"}
2020-09-08 09:20:53 +08:00
//
// Otherwise the Get function would return:
//
2022-09-16 21:35:05 +08:00
// "a b c"
[110] Default Values Specify Type This patch adds a feature, if enabled, will infer a value's type from its default value no matter from where else the value is set. This is particularly important when working with environment variables. For example: package main import ( "fmt" "os" "github.com/spf13/viper" ) func print(name string, val interface{}) { fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val) } func main() { viper.BindEnv("mykey", "MYPREFIX_MYKEY") viper.SetDefault("mykey", []string{}) os.Setenv("MYPREFIX_MYKEY", "a b c") v1 := viper.GetStringSlice("mykey") v2 := viper.Get("mykey") print("v1", v1) print("v2", v2) } When this program is executed the following is emitted: [0]akutz@pax:ex$ ./ex1 v1 []string [a b c] v2 string a b c [0]akutz@pax:ex$ You may wonder, why is this important? Just use the GetStringSlice function. Well, it *becomes* important when dealing with marshaling. If we update the above program to this: package main import ( "fmt" "os" "github.com/spf13/viper" ) type Data struct { MyKey []string } func print(name string, val interface{}) { fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val) } func main() { viper.BindEnv("mykey", "MYPREFIX_MYKEY") viper.SetDefault("mykey", []string{}) os.Setenv("MYPREFIX_MYKEY", "a b c") v1 := viper.GetStringSlice("mykey") v2 := viper.Get("mykey") print("v1", v1) print("v2", v2) d := &Data{} viper.Marshal(d) print("d.MyKey", d.MyKey) } Now we can see the issue when we execute the updated program: [0]akutz@pax:ex$ ./ex2 v1 []string [a b c] v2 string a b c d.MyKey []string [] [0]akutz@pax:ex$ The marshalled data structure's field MyKey is empty when in fact it should have a string slice equal to, in value, []string {"a", "b", "c"}. The problem is that viper's Marshal function calls AllSettings which ultimately uses the Get function. The Get function does try to infer the value's type, but it does so using the type of the value retrieved using this logic: Get has the behavior of returning the value associated with the first place from where it is set. Viper will check in the following order: * override * flag * env * config file * key/value store * default While the above order is the one we want when retrieving the values, this patch enables users to decide if it's the order they want to be used when inferring a value's type. To that end the function SetTypeByDefaultValue is introduced. When SetTypeByDefaultValue(true) is called, a call to the Get function will now first check a key's default value, if set, when inferring a value's type. This is demonstrated using a modified version of the same program above: package main import ( "fmt" "os" "github.com/spf13/viper" ) type Data struct { MyKey []string } func print(name string, val interface{}) { fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val) } func main() { viper.BindEnv("mykey", "MYPREFIX_MYKEY") viper.SetDefault("mykey", []string{}) os.Setenv("MYPREFIX_MYKEY", "a b c") v1 := viper.GetStringSlice("mykey") v2 := viper.Get("mykey") print("v1", v1) print("v2", v2) d1 := &Data{} viper.Marshal(d1) print("d1.MyKey", d1.MyKey) viper.SetTypeByDefaultValue(true) d2 := &Data{} viper.Marshal(d2) print("d2.MyKey", d2.MyKey) } Now the following is emitted: [0]akutz@pax:ex$ ./ex3 v1 []string [a b c] v2 string a b c d1.MyKey []string [] d2.MyKey []string [a b c] [0]akutz@pax:ex$
2015-08-29 10:54:20 -05:00
func (v *Viper) SetTypeByDefaultValue(enable bool) {
v.typeByDefValue = enable
}
2016-08-06 18:06:49 +02:00
// GetViper gets the global Viper instance.
func GetViper() *Viper {
return v
}
// Get can retrieve any value given the key to use.
// Get is case-insensitive for a key.
// Get has the behavior of returning the value associated with the first
// place from where it is set. Viper will check in the following order:
// override, flag, env, config file, key/value store, default
//
// Get returns an interface. For a specific value use one of the Get____ methods.
func Get(key string) interface{} { return v.Get(key) }
2020-09-08 09:20:53 +08:00
// Get can retrieve any value given the key to use.
// Get is case-insensitive for a key.
// Get has the behavior of returning the value associated with the first
// place from where it is set. Viper will check in the following order:
// override, flag, env, config file, key/value store, default
//
// Get returns an interface. For a specific value use one of the Get____ methods.
func (v *Viper) Get(key string) interface{} {
[110] Default Values Specify Type This patch adds a feature, if enabled, will infer a value's type from its default value no matter from where else the value is set. This is particularly important when working with environment variables. For example: package main import ( "fmt" "os" "github.com/spf13/viper" ) func print(name string, val interface{}) { fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val) } func main() { viper.BindEnv("mykey", "MYPREFIX_MYKEY") viper.SetDefault("mykey", []string{}) os.Setenv("MYPREFIX_MYKEY", "a b c") v1 := viper.GetStringSlice("mykey") v2 := viper.Get("mykey") print("v1", v1) print("v2", v2) } When this program is executed the following is emitted: [0]akutz@pax:ex$ ./ex1 v1 []string [a b c] v2 string a b c [0]akutz@pax:ex$ You may wonder, why is this important? Just use the GetStringSlice function. Well, it *becomes* important when dealing with marshaling. If we update the above program to this: package main import ( "fmt" "os" "github.com/spf13/viper" ) type Data struct { MyKey []string } func print(name string, val interface{}) { fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val) } func main() { viper.BindEnv("mykey", "MYPREFIX_MYKEY") viper.SetDefault("mykey", []string{}) os.Setenv("MYPREFIX_MYKEY", "a b c") v1 := viper.GetStringSlice("mykey") v2 := viper.Get("mykey") print("v1", v1) print("v2", v2) d := &Data{} viper.Marshal(d) print("d.MyKey", d.MyKey) } Now we can see the issue when we execute the updated program: [0]akutz@pax:ex$ ./ex2 v1 []string [a b c] v2 string a b c d.MyKey []string [] [0]akutz@pax:ex$ The marshalled data structure's field MyKey is empty when in fact it should have a string slice equal to, in value, []string {"a", "b", "c"}. The problem is that viper's Marshal function calls AllSettings which ultimately uses the Get function. The Get function does try to infer the value's type, but it does so using the type of the value retrieved using this logic: Get has the behavior of returning the value associated with the first place from where it is set. Viper will check in the following order: * override * flag * env * config file * key/value store * default While the above order is the one we want when retrieving the values, this patch enables users to decide if it's the order they want to be used when inferring a value's type. To that end the function SetTypeByDefaultValue is introduced. When SetTypeByDefaultValue(true) is called, a call to the Get function will now first check a key's default value, if set, when inferring a value's type. This is demonstrated using a modified version of the same program above: package main import ( "fmt" "os" "github.com/spf13/viper" ) type Data struct { MyKey []string } func print(name string, val interface{}) { fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val) } func main() { viper.BindEnv("mykey", "MYPREFIX_MYKEY") viper.SetDefault("mykey", []string{}) os.Setenv("MYPREFIX_MYKEY", "a b c") v1 := viper.GetStringSlice("mykey") v2 := viper.Get("mykey") print("v1", v1) print("v2", v2) d1 := &Data{} viper.Marshal(d1) print("d1.MyKey", d1.MyKey) viper.SetTypeByDefaultValue(true) d2 := &Data{} viper.Marshal(d2) print("d2.MyKey", d2.MyKey) } Now the following is emitted: [0]akutz@pax:ex$ ./ex3 v1 []string [a b c] v2 string a b c d1.MyKey []string [] d2.MyKey []string [a b c] [0]akutz@pax:ex$
2015-08-29 10:54:20 -05:00
lcaseKey := strings.ToLower(key)
2020-09-08 10:34:59 +08:00
val := v.find(lcaseKey, true)
if val == nil {
return nil
}
if v.typeByDefValue {
valType := val
path := strings.Split(lcaseKey, v.keyDelim)
defVal := v.searchMap(v.defaults, path)
if defVal != nil {
[110] Default Values Specify Type This patch adds a feature, if enabled, will infer a value's type from its default value no matter from where else the value is set. This is particularly important when working with environment variables. For example: package main import ( "fmt" "os" "github.com/spf13/viper" ) func print(name string, val interface{}) { fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val) } func main() { viper.BindEnv("mykey", "MYPREFIX_MYKEY") viper.SetDefault("mykey", []string{}) os.Setenv("MYPREFIX_MYKEY", "a b c") v1 := viper.GetStringSlice("mykey") v2 := viper.Get("mykey") print("v1", v1) print("v2", v2) } When this program is executed the following is emitted: [0]akutz@pax:ex$ ./ex1 v1 []string [a b c] v2 string a b c [0]akutz@pax:ex$ You may wonder, why is this important? Just use the GetStringSlice function. Well, it *becomes* important when dealing with marshaling. If we update the above program to this: package main import ( "fmt" "os" "github.com/spf13/viper" ) type Data struct { MyKey []string } func print(name string, val interface{}) { fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val) } func main() { viper.BindEnv("mykey", "MYPREFIX_MYKEY") viper.SetDefault("mykey", []string{}) os.Setenv("MYPREFIX_MYKEY", "a b c") v1 := viper.GetStringSlice("mykey") v2 := viper.Get("mykey") print("v1", v1) print("v2", v2) d := &Data{} viper.Marshal(d) print("d.MyKey", d.MyKey) } Now we can see the issue when we execute the updated program: [0]akutz@pax:ex$ ./ex2 v1 []string [a b c] v2 string a b c d.MyKey []string [] [0]akutz@pax:ex$ The marshalled data structure's field MyKey is empty when in fact it should have a string slice equal to, in value, []string {"a", "b", "c"}. The problem is that viper's Marshal function calls AllSettings which ultimately uses the Get function. The Get function does try to infer the value's type, but it does so using the type of the value retrieved using this logic: Get has the behavior of returning the value associated with the first place from where it is set. Viper will check in the following order: * override * flag * env * config file * key/value store * default While the above order is the one we want when retrieving the values, this patch enables users to decide if it's the order they want to be used when inferring a value's type. To that end the function SetTypeByDefaultValue is introduced. When SetTypeByDefaultValue(true) is called, a call to the Get function will now first check a key's default value, if set, when inferring a value's type. This is demonstrated using a modified version of the same program above: package main import ( "fmt" "os" "github.com/spf13/viper" ) type Data struct { MyKey []string } func print(name string, val interface{}) { fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val) } func main() { viper.BindEnv("mykey", "MYPREFIX_MYKEY") viper.SetDefault("mykey", []string{}) os.Setenv("MYPREFIX_MYKEY", "a b c") v1 := viper.GetStringSlice("mykey") v2 := viper.Get("mykey") print("v1", v1) print("v2", v2) d1 := &Data{} viper.Marshal(d1) print("d1.MyKey", d1.MyKey) viper.SetTypeByDefaultValue(true) d2 := &Data{} viper.Marshal(d2) print("d2.MyKey", d2.MyKey) } Now the following is emitted: [0]akutz@pax:ex$ ./ex3 v1 []string [a b c] v2 string a b c d1.MyKey []string [] d2.MyKey []string [a b c] [0]akutz@pax:ex$
2015-08-29 10:54:20 -05:00
valType = defVal
}
switch valType.(type) {
case bool:
return cast.ToBool(val)
case string:
return cast.ToString(val)
case int32, int16, int8, int:
return cast.ToInt(val)
2020-09-08 10:34:59 +08:00
case uint:
return cast.ToUint(val)
case uint32:
return cast.ToUint32(val)
case uint64:
return cast.ToUint64(val)
case int64:
return cast.ToInt64(val)
case float64, float32:
return cast.ToFloat64(val)
case time.Time:
return cast.ToTime(val)
case time.Duration:
return cast.ToDuration(val)
case []string:
return cast.ToStringSlice(val)
2020-09-08 10:34:59 +08:00
case []int:
return cast.ToIntSlice(val)
}
}
return val
}
// 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) }
2020-09-08 09:20:53 +08:00
// Sub returns new Viper instance representing a sub tree of this instance.
// Sub is case-insensitive for a key.
func (v *Viper) Sub(key string) *Viper {
subv := New()
data := v.Get(key)
2016-10-05 18:22:39 -05:00
if data == nil {
return nil
}
if reflect.TypeOf(data).Kind() == reflect.Map {
subv.config = cast.ToStringMap(data)
return subv
}
return nil
}
// GetString returns the value associated with the key as a string.
2022-09-16 21:35:05 +08:00
func GetString(key string, defaultValue string) string { return v.GetString(key, defaultValue) }
2020-09-08 09:20:53 +08:00
// GetString returns the value associated with the key as a string.
2022-09-16 21:35:05 +08:00
func (v *Viper) GetString(key string, defaultValue string) string {
if v.IsSet(key) {
return cast.ToString(v.Get(key))
}
return defaultValue
2014-04-04 17:21:59 -04:00
}
// GetBool returns the value associated with the key as a boolean.
func GetBool(key string) bool { return v.GetBool(key) }
2020-09-08 09:20:53 +08:00
// GetBool returns the value associated with the key as a boolean.
func (v *Viper) GetBool(key string) bool {
return cast.ToBool(v.Get(key))
2014-04-04 17:21:59 -04:00
}
// GetInt returns the value associated with the key as an integer.
2022-09-16 21:35:05 +08:00
func GetInt(key string, defaultValue int) int { return v.GetInt(key, defaultValue) }
2020-09-08 09:20:53 +08:00
// GetInt returns the value associated with the key as an integer.
2022-09-16 21:35:05 +08:00
func (v *Viper) GetInt(key string, defaultValue int) int {
if v.IsSet(key) {
return cast.ToInt(v.Get(key))
}
return defaultValue
2014-04-04 17:21:59 -04:00
}
2018-05-05 21:38:06 -04:00
// GetInt32 returns the value associated with the key as an integer.
2022-09-16 21:35:05 +08:00
func GetInt32(key string, defaultValue int32) int32 { return v.GetInt32(key, defaultValue) }
2020-09-08 09:20:53 +08:00
// GetInt32 returns the value associated with the key as an integer.
2022-09-16 21:35:05 +08:00
func (v *Viper) GetInt32(key string, defaultValue int32) int32 {
if v.IsSet(key) {
return cast.ToInt32(v.Get(key))
}
return defaultValue
2018-05-05 21:38:06 -04:00
}
// GetInt64 returns the value associated with the key as an integer.
2022-09-16 21:35:05 +08:00
func GetInt64(key string, defaultValue int64) int64 { return v.GetInt64(key, defaultValue) }
2020-09-08 09:20:53 +08:00
// GetInt64 returns the value associated with the key as an integer.
2022-09-16 21:35:05 +08:00
func (v *Viper) GetInt64(key string, defaultValue int64) int64 {
if v.IsSet(key) {
return cast.ToInt64(v.Get(key))
}
return defaultValue
2016-08-05 15:16:55 +08:00
}
2020-09-08 10:34:59 +08:00
// GetUint returns the value associated with the key as an unsigned integer.
2022-09-16 21:35:05 +08:00
func GetUint(key string, defaultValue uint) uint { return v.GetUint(key, defaultValue) }
2020-09-08 10:34:59 +08:00
// GetUint returns the value associated with the key as an unsigned integer.
2022-09-16 21:35:05 +08:00
func (v *Viper) GetUint(key string, defaultValue uint) uint {
if v.IsSet(key) {
return cast.ToUint(v.Get(key))
}
return defaultValue
2020-09-08 10:34:59 +08:00
}
// GetUint32 returns the value associated with the key as an unsigned integer.
2022-09-16 21:35:05 +08:00
func GetUint32(key string, defaultValue uint32) uint32 { return v.GetUint32(key, defaultValue) }
2020-09-08 10:34:59 +08:00
// GetUint32 returns the value associated with the key as an unsigned integer.
2022-09-16 21:35:05 +08:00
func (v *Viper) GetUint32(key string, defaultValue uint32) uint32 {
if v.IsSet(key) {
return cast.ToUint32(v.Get(key))
}
return defaultValue
2020-09-08 10:34:59 +08:00
}
// GetUint64 returns the value associated with the key as an unsigned integer.
2022-09-16 21:35:05 +08:00
func GetUint64(key string, defaultValue uint64) uint64 { return v.GetUint64(key, defaultValue) }
2020-09-08 10:34:59 +08:00
// GetUint64 returns the value associated with the key as an unsigned integer.
2022-09-16 21:35:05 +08:00
func (v *Viper) GetUint64(key string, defaultValue uint64) uint64 {
if v.IsSet(key) {
return cast.ToUint64(v.Get(key))
}
return defaultValue
2020-09-08 10:34:59 +08:00
}
// GetFloat64 returns the value associated with the key as a float64.
2022-09-16 21:35:05 +08:00
func GetFloat64(key string, defaultValue float64) float64 { return v.GetFloat64(key, defaultValue) }
2020-09-08 09:20:53 +08:00
// GetFloat64 returns the value associated with the key as a float64.
2022-09-16 21:35:05 +08:00
func (v *Viper) GetFloat64(key string, defaultValue float64) float64 {
if v.IsSet(key) {
return cast.ToFloat64(v.Get(key))
}
return defaultValue
2014-04-04 17:21:59 -04:00
}
// GetTime returns the value associated with the key as time.
2022-09-16 21:35:05 +08:00
func GetTime(key string, defaultValue time.Time) time.Time { return v.GetTime(key, defaultValue) }
2020-09-08 09:20:53 +08:00
// GetTime returns the value associated with the key as time.
2022-09-16 21:35:05 +08:00
func (v *Viper) GetTime(key string, defaultValue time.Time) time.Time {
if v.IsSet(key) {
return cast.ToTime(v.Get(key))
}
return defaultValue
2014-04-04 17:21:59 -04:00
}
// GetDuration returns the value associated with the key as a duration.
2022-09-16 21:35:05 +08:00
func GetDuration(key string, defaultValue time.Duration) time.Duration {
return v.GetDuration(key, defaultValue)
}
2020-09-08 09:20:53 +08:00
// GetDuration returns the value associated with the key as a duration.
2022-09-16 21:35:05 +08:00
func (v *Viper) GetDuration(key string, defaultValue time.Duration) time.Duration {
if v.IsSet(key) {
return cast.ToDuration(v.Get(key))
}
return defaultValue
2015-02-18 19:03:20 -08:00
}
2020-09-08 10:34:59 +08:00
// GetIntSlice returns the value associated with the key as a slice of int values.
2022-09-16 21:35:05 +08:00
func GetIntSlice(key string, defaultValue []int) []int { return v.GetIntSlice(key, defaultValue) }
2020-09-08 10:34:59 +08:00
// GetIntSlice returns the value associated with the key as a slice of int values.
2022-09-16 21:35:05 +08:00
func (v *Viper) GetIntSlice(key string, defaultValue []int) []int {
if v.IsSet(key) {
return cast.ToIntSlice(v.Get(key))
}
return defaultValue
2020-09-08 10:34:59 +08:00
}
// GetStringSlice returns the value associated with the key as a slice of strings.
2022-09-16 21:35:05 +08:00
func GetStringSlice(key string, defaultValue []string) []string {
return v.GetStringSlice(key, defaultValue)
}
2020-09-08 09:20:53 +08:00
// GetStringSlice returns the value associated with the key as a slice of strings.
2022-09-16 21:35:05 +08:00
func (v *Viper) GetStringSlice(key string, defaultValue []string) []string {
if v.IsSet(key) {
return cast.ToStringSlice(v.Get(key))
}
return defaultValue
2014-04-05 01:19:39 -04:00
}
// GetStringMap returns the value associated with the key as a map of interfaces.
2022-09-16 21:35:05 +08:00
func GetStringMap(key string, defaultValue map[string]interface{}) map[string]interface{} {
return v.GetStringMap(key, defaultValue)
}
2020-09-08 09:20:53 +08:00
// GetStringMap returns the value associated with the key as a map of interfaces.
2022-09-16 21:35:05 +08:00
func (v *Viper) GetStringMap(key string, defaultValue map[string]interface{}) map[string]interface{} {
if v.IsSet(key) {
return cast.ToStringMap(v.Get(key))
}
return defaultValue
2014-04-05 01:19:39 -04:00
}
// GetStringMapString returns the value associated with the key as a map of strings.
2022-09-16 21:35:05 +08:00
func GetStringMapString(key string, defaultValue map[string]string) map[string]string {
return v.GetStringMapString(key, defaultValue)
}
2020-09-08 09:20:53 +08:00
// GetStringMapString returns the value associated with the key as a map of strings.
2022-09-16 21:35:05 +08:00
func (v *Viper) GetStringMapString(key string, defaultValue map[string]string) map[string]string {
if v.IsSet(key) {
return cast.ToStringMapString(v.Get(key))
}
return defaultValue
2014-04-04 17:21:59 -04:00
}
// GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings.
2022-09-16 21:35:05 +08:00
func GetStringMapStringSlice(key string, defaultValue map[string][]string) map[string][]string {
return v.GetStringMapStringSlice(key, defaultValue)
}
2020-09-08 09:20:53 +08:00
// GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings.
2022-09-16 21:35:05 +08:00
func (v *Viper) GetStringMapStringSlice(key string, defaultValue map[string][]string) map[string][]string {
if v.IsSet(key) {
return cast.ToStringMapStringSlice(v.Get(key))
}
return defaultValue
2015-07-30 15:27:34 -05:00
}
// GetSizeInBytes returns the size of the value associated with the given key
// in bytes.
2022-09-16 21:35:05 +08:00
func GetSizeInBytes(key string, defaultValue uint) uint { return v.GetSizeInBytes(key, defaultValue) }
2020-09-08 09:20:53 +08:00
// GetSizeInBytes returns the size of the value associated with the given key
// in bytes.
2022-09-16 21:35:05 +08:00
func (v *Viper) GetSizeInBytes(key string, defaultValue uint) uint {
if v.IsSet(key) {
sizeStr := cast.ToString(v.Get(key))
return parseSizeInBytes(sizeStr)
}
return defaultValue
}
// UnmarshalKey takes a single key and unmarshals it into a Struct.
func UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConfigOption) error {
return v.UnmarshalKey(key, rawVal, opts...)
}
2020-09-08 09:20:53 +08:00
// UnmarshalKey takes a single key and unmarshals it into a Struct.
func (v *Viper) UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConfigOption) error {
2020-09-08 10:34:59 +08:00
return decode(v.Get(key), defaultDecoderConfig(rawVal, opts...))
}
// Unmarshal unmarshals the config into a Struct. Make sure that the tags
// on the fields of the structure are properly set.
func Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error {
return v.Unmarshal(rawVal, opts...)
}
2020-09-08 09:20:53 +08:00
// Unmarshal unmarshals the config into a Struct. Make sure that the tags
// on the fields of the structure are properly set.
func (v *Viper) Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error {
2020-09-08 10:34:59 +08:00
return decode(v.AllSettings(), defaultDecoderConfig(rawVal, opts...))
}
// defaultDecoderConfig returns default mapsstructure.DecoderConfig with suppot
// of time.Duration values & string slices
func defaultDecoderConfig(output interface{}, opts ...DecoderConfigOption) *mapstructure.DecoderConfig {
c := &mapstructure.DecoderConfig{
Metadata: nil,
Result: output,
WeaklyTypedInput: true,
2017-09-29 23:06:42 +02:00
DecodeHook: mapstructure.ComposeDecodeHookFunc(
mapstructure.StringToTimeDurationHookFunc(),
mapstructure.StringToSliceHookFunc(","),
),
}
for _, opt := range opts {
opt(c)
}
return c
}
// A wrapper around mapstructure.Decode that mimics the WeakDecode functionality
func decode(input interface{}, config *mapstructure.DecoderConfig) error {
decoder, err := mapstructure.NewDecoder(config)
if err != nil {
return err
}
return decoder.Decode(input)
}
// UnmarshalExact unmarshals the config into a Struct, erroring if a field is nonexistent
// in the destination struct.
2020-09-08 10:34:59 +08:00
func UnmarshalExact(rawVal interface{}, opts ...DecoderConfigOption) error {
return v.UnmarshalExact(rawVal, opts...)
}
2020-09-08 10:34:59 +08:00
// UnmarshalExact unmarshals the config into a Struct, erroring if a field is nonexistent
// in the destination struct.
func (v *Viper) UnmarshalExact(rawVal interface{}, opts ...DecoderConfigOption) error {
config := defaultDecoderConfig(rawVal, opts...)
config.ErrorUnused = true
2020-09-08 10:34:59 +08:00
return decode(v.AllSettings(), config)
}
// 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) }
2020-09-08 09:20:53 +08:00
// BindPFlags binds a full flag set to the configuration, using each flag's long
// name as the config key.
func (v *Viper) BindPFlags(flags *pflag.FlagSet) error {
return v.BindFlagValues(pflagValueSet{flags})
}
// BindPFlag binds a specific key to a pflag (as used by cobra).
2016-08-05 12:25:24 +05:00
// Example (where serverCmd is a Cobra instance):
//
2022-09-16 21:35:05 +08:00
// serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
// Viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
func BindPFlag(key string, flag *pflag.Flag) error { return v.BindPFlag(key, flag) }
2020-09-08 09:20:53 +08:00
// BindPFlag binds a specific key to a pflag (as used by cobra).
// Example (where serverCmd is a Cobra instance):
//
2022-09-16 21:35:05 +08:00
// serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
// Viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
func (v *Viper) BindPFlag(key string, flag *pflag.Flag) error {
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) }
2020-09-08 09:20:53 +08:00
// BindFlagValues binds a full FlagValue set to the configuration, using each flag's long
// name as the config key.
func (v *Viper) BindFlagValues(flags FlagValueSet) (err error) {
flags.VisitAll(func(flag FlagValue) {
if err = v.BindFlagValue(flag.Name(), flag); err != nil {
return
}
})
return nil
}
// BindFlagValue binds a specific key to a FlagValue.
// Example (where serverCmd is a Cobra instance):
2014-07-11 10:42:07 -04:00
//
2022-09-16 21:35:05 +08:00
// serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
// Viper.BindFlagValue("port", serverCmd.Flags().Lookup("port"))
func BindFlagValue(key string, flag FlagValue) error { return v.BindFlagValue(key, flag) }
2020-09-08 09:20:53 +08:00
// BindFlagValue binds a specific key to a FlagValue.
// Example (where serverCmd is a Cobra instance):
//
2022-09-16 21:35:05 +08:00
// serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
// Viper.BindFlagValue("port", serverCmd.Flags().Lookup("port"))
func (v *Viper) BindFlagValue(key string, flag FlagValue) error {
if flag == nil {
return fmt.Errorf("flag for %q is nil", key)
}
v.pflags[strings.ToLower(key)] = flag
return nil
}
// BindEnv binds a Viper key to a ENV variable.
// ENV variables are case sensitive.
2014-09-27 14:03:00 -07:00
// If only a key is provided, it will use the env key matching the key, uppercased.
// EnvPrefix will be used when set when env name is not provided.
func BindEnv(input ...string) error { return v.BindEnv(input...) }
2020-09-08 09:20:53 +08:00
// BindEnv binds a Viper key to a ENV variable.
// ENV variables are case sensitive.
// If only a key is provided, it will use the env key matching the key, uppercased.
// EnvPrefix will be used when set when env name is not provided.
func (v *Viper) BindEnv(input ...string) error {
2014-09-27 14:03:00 -07:00
var key, envkey string
if len(input) == 0 {
return fmt.Errorf("BindEnv missing key to bind to")
}
key = strings.ToLower(input[0])
2014-09-27 14:03:00 -07:00
if len(input) == 1 {
envkey = v.mergeWithEnvPrefix(key)
2014-09-27 14:03:00 -07:00
} else {
envkey = input[1]
}
v.env[key] = envkey
2014-09-27 14:03:00 -07:00
return nil
}
// Given a key, find the value.
2020-09-08 10:34:59 +08:00
//
// Viper will check to see if an alias exists first.
2020-09-08 10:34:59 +08:00
// Viper will then check in the following order:
// flag, env, config file, key/value store.
// Lastly, if no value was found and flagDefault is true, and if the key
// corresponds to a flag, the flag's default value is returned.
//
// Note: this assumes a lower-cased key given.
2020-09-08 10:34:59 +08:00
func (v *Viper) find(lcaseKey string, flagDefault bool) interface{} {
var (
val interface{}
exists bool
path = strings.Split(lcaseKey, v.keyDelim)
nested = len(path) > 1
)
2014-04-04 17:21:59 -04:00
// compute the path through the nested maps to the nested value
if nested && v.isPathShadowedInDeepMap(path, castMapStringToMapInterface(v.aliases)) != "" {
return nil
}
2014-04-05 01:19:39 -04:00
// if the requested key is an alias, then return the proper key
lcaseKey = v.realKey(lcaseKey)
path = strings.Split(lcaseKey, v.keyDelim)
nested = len(path) > 1
2014-04-04 17:21:59 -04:00
// Set() override first
val = v.searchMap(v.override, path)
if val != nil {
return val
}
if nested && v.isPathShadowedInDeepMap(path, v.override) != "" {
return nil
}
// PFlag override next
flag, exists := v.pflags[lcaseKey]
if exists && flag.HasChanged() {
switch flag.ValueType() {
case "int", "int8", "int16", "int32", "int64":
return cast.ToInt(flag.ValueString())
case "bool":
return cast.ToBool(flag.ValueString())
case "stringSlice":
s := strings.TrimPrefix(flag.ValueString(), "[")
2017-04-17 10:08:15 +02:00
s = strings.TrimSuffix(s, "]")
res, _ := readAsCSV(s)
return res
2020-09-08 10:34:59 +08:00
case "intSlice":
s := strings.TrimPrefix(flag.ValueString(), "[")
s = strings.TrimSuffix(s, "]")
res, _ := readAsCSV(s)
return cast.ToIntSlice(res)
case "stringToString":
return stringToStringConv(flag.ValueString())
default:
return flag.ValueString()
}
}
if nested && v.isPathShadowedInFlatMap(path, v.pflags) != "" {
return nil
2014-04-04 17:21:59 -04:00
}
// Env override next
if v.automaticEnvApplied {
// even if it hasn't been registered, if automaticEnv is used,
// check any Get request
2020-09-08 10:34:59 +08:00
if val, ok := v.getEnv(v.mergeWithEnvPrefix(lcaseKey)); ok {
return val
}
if nested && v.isPathShadowedInAutoEnv(path) != "" {
return nil
}
}
envkey, exists := v.env[lcaseKey]
2014-09-27 14:03:00 -07:00
if exists {
2020-09-08 10:34:59 +08:00
if val, ok := v.getEnv(envkey); ok {
2014-09-27 14:03:00 -07:00
return val
}
}
if nested && v.isPathShadowedInFlatMap(path, v.env) != "" {
return nil
}
2014-09-27 14:03:00 -07:00
// Config file next
val = v.searchMapWithPathPrefixes(v.config, path)
if val != nil {
2014-04-04 17:21:59 -04:00
return val
}
if nested && v.isPathShadowedInDeepMap(path, v.config) != "" {
return nil
}
// K/V store next
val = v.searchMap(v.kvstore, path)
if val != nil {
2014-10-24 15:38:01 -04:00
return val
}
if nested && v.isPathShadowedInDeepMap(path, v.kvstore) != "" {
return nil
}
2014-10-24 15:38:01 -04:00
// Default next
val = v.searchMap(v.defaults, path)
if val != nil {
2014-04-04 17:21:59 -04:00
return val
}
if nested && v.isPathShadowedInDeepMap(path, v.defaults) != "" {
return nil
}
2020-09-08 10:34:59 +08:00
if flagDefault {
// last chance: if no value is found and a flag does exist for the key,
// get the flag's default value even if the flag's value has not been set.
if flag, exists := v.pflags[lcaseKey]; exists {
switch flag.ValueType() {
case "int", "int8", "int16", "int32", "int64":
return cast.ToInt(flag.ValueString())
case "bool":
return cast.ToBool(flag.ValueString())
case "stringSlice":
s := strings.TrimPrefix(flag.ValueString(), "[")
s = strings.TrimSuffix(s, "]")
res, _ := readAsCSV(s)
return res
case "intSlice":
s := strings.TrimPrefix(flag.ValueString(), "[")
s = strings.TrimSuffix(s, "]")
res, _ := readAsCSV(s)
return cast.ToIntSlice(res)
case "stringToString":
return stringToStringConv(flag.ValueString())
default:
return flag.ValueString()
}
}
2020-09-08 10:34:59 +08:00
// last item, no need to check shadowing
}
2014-04-04 17:21:59 -04:00
return nil
}
2017-04-17 10:08:15 +02:00
func readAsCSV(val string) ([]string, error) {
if val == "" {
return []string{}, nil
}
stringReader := strings.NewReader(val)
csvReader := csv.NewReader(stringReader)
return csvReader.Read()
}
2020-09-08 10:34:59 +08:00
// mostly copied from pflag's implementation of this operation here https://github.com/spf13/pflag/blob/master/string_to_string.go#L79
// alterations are: errors are swallowed, map[string]interface{} is returned in order to enable cast.ToStringMap
func stringToStringConv(val string) interface{} {
val = strings.Trim(val, "[]")
// An empty string would cause an empty map
if len(val) == 0 {
return map[string]interface{}{}
}
r := csv.NewReader(strings.NewReader(val))
ss, err := r.Read()
if err != nil {
return nil
}
out := make(map[string]interface{}, len(ss))
for _, pair := range ss {
kv := strings.SplitN(pair, "=", 2)
if len(kv) != 2 {
return nil
}
out[kv[0]] = kv[1]
}
return out
}
// 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) }
2020-09-08 09:20:53 +08:00
// IsSet checks to see if the key has been set in any of the data locations.
// IsSet is case-insensitive for a key.
func (v *Viper) IsSet(key string) bool {
lcaseKey := strings.ToLower(key)
2020-09-08 10:34:59 +08:00
val := v.find(lcaseKey, false)
return val != nil
2014-04-04 17:21:59 -04:00
}
// AutomaticEnv has Viper check ENV variables for all.
// keys set in config, default & flags
func AutomaticEnv() { v.AutomaticEnv() }
2020-09-08 09:20:53 +08:00
// AutomaticEnv has Viper check ENV variables for all.
// keys set in config, default & flags
func (v *Viper) AutomaticEnv() {
v.automaticEnvApplied = true
}
2015-03-06 11:21:17 -08:00
// SetEnvKeyReplacer sets the strings.Replacer on the viper object
// Useful for mapping an environmental variable to a key that does
// not match it.
2015-03-06 11:21:17 -08:00
func SetEnvKeyReplacer(r *strings.Replacer) { v.SetEnvKeyReplacer(r) }
2020-09-08 09:20:53 +08:00
// SetEnvKeyReplacer sets the strings.Replacer on the viper object
// Useful for mapping an environmental variable to a key that does
// not match it.
func (v *Viper) SetEnvKeyReplacer(r *strings.Replacer) {
2015-03-06 11:21:17 -08:00
v.envKeyReplacer = r
}
2020-09-08 09:20:53 +08:00
// RegisterAlias provide another accessor for the same key.
2014-07-11 10:42:07 -04:00
// This enables one to change a name without breaking the application
func RegisterAlias(alias string, key string) { v.RegisterAlias(alias, key) }
2020-09-08 09:20:53 +08:00
// RegisterAlias provide another accessor for the same key.
// This enables one to change a name without breaking the application
func (v *Viper) RegisterAlias(alias string, key string) {
v.registerAlias(alias, strings.ToLower(key))
2014-04-05 01:19:39 -04:00
}
func (v *Viper) registerAlias(alias string, key string) {
2014-04-05 01:19:39 -04:00
alias = strings.ToLower(alias)
if alias != key && alias != v.realKey(key) {
_, exists := v.aliases[alias]
2014-04-05 01:19:39 -04:00
if !exists {
// if we alias something that exists in one of the maps to another
// name, we'll never be able to get that value using the original
// name, so move the config value to the new realkey.
if val, ok := v.config[alias]; ok {
delete(v.config, alias)
v.config[key] = val
}
if val, ok := v.kvstore[alias]; ok {
delete(v.kvstore, alias)
v.kvstore[key] = val
2014-10-24 15:38:01 -04:00
}
if val, ok := v.defaults[alias]; ok {
delete(v.defaults, alias)
v.defaults[key] = val
}
if val, ok := v.override[alias]; ok {
delete(v.override, alias)
v.override[key] = val
}
v.aliases[alias] = key
2014-04-05 01:19:39 -04:00
}
} else {
2020-09-08 09:20:53 +08:00
v.logger.Warningf("Creating circular reference alias %s key %s with realKey: %s", alias, key, v.realKey(key))
2014-04-05 01:19:39 -04:00
}
}
func (v *Viper) realKey(key string) string {
newkey, exists := v.aliases[key]
2014-04-05 01:19:39 -04:00
if exists {
2020-09-08 09:20:53 +08:00
v.logger.Debugf("Alias key %s to: %s", key, newkey)
return v.realKey(newkey)
2014-04-05 01:19:39 -04:00
}
return key
2014-04-04 17:21:59 -04:00
}
// 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) }
2020-09-08 09:20:53 +08:00
// InConfig checks to see if the given key (or an alias) is in the config file.
func (v *Viper) InConfig(key string) bool {
2014-04-05 01:19:39 -04:00
// if the requested key is an alias, then return the proper key
key = v.realKey(key)
2014-04-05 01:19:39 -04:00
_, exists := v.config[key]
2014-04-04 17:21:59 -04:00
return exists
}
// SetDefault sets the default value for this key.
// SetDefault is case-insensitive for a key.
2014-07-11 10:42:07 -04:00
// 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) }
2020-09-08 09:20:53 +08:00
// SetDefault sets the default value for this key.
// SetDefault is case-insensitive for a key.
// Default only used when no value is provided by the user via flag, config or ENV.
func (v *Viper) SetDefault(key string, value interface{}) {
2014-04-04 17:21:59 -04:00
// If alias passed in, then set the proper default
key = v.realKey(strings.ToLower(key))
value = toCaseInsensitiveValue(value)
path := strings.Split(key, v.keyDelim)
lastKey := strings.ToLower(path[len(path)-1])
deepestMap := deepSearch(v.defaults, path[0:len(path)-1])
// set innermost value
deepestMap[lastKey] = value
2014-04-04 17:21:59 -04:00
}
// Set sets the value for the key in the override register.
// Set is case-insensitive for a key.
2014-10-24 15:38:01 -04:00
// 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) }
2020-09-08 09:20:53 +08:00
// Set sets the value for the key in the override register.
// Set is case-insensitive for a key.
// Will be used instead of values obtained via
// flags, config file, ENV, default, or key/value store.
func (v *Viper) Set(key string, value interface{}) {
2014-04-04 17:21:59 -04:00
// If alias passed in, then set the proper override
key = v.realKey(strings.ToLower(key))
value = toCaseInsensitiveValue(value)
path := strings.Split(key, v.keyDelim)
lastKey := strings.ToLower(path[len(path)-1])
deepestMap := deepSearch(v.override, path[0:len(path)-1])
// set innermost value
deepestMap[lastKey] = value
}
// ReadInConfig will discover and load the configuration file from disk
2014-10-24 15:38:01 -04:00
// and key/value stores, searching in one of the defined paths.
func ReadInConfig() error { return v.ReadInConfig() }
2020-09-08 09:20:53 +08:00
// ReadInConfig will discover and load the configuration file from disk
// and key/value stores, searching in one of the defined paths.
func (v *Viper) ReadInConfig() error {
2020-09-08 09:20:53 +08:00
v.logger.Infof("Attempting to read in config file")
filename, err := v.getConfigFile()
if err != nil {
return err
}
if !stringInSlice(v.getConfigType(), SupportedExts) {
return UnsupportedConfigError(v.getConfigType())
2014-04-04 17:21:59 -04:00
}
2020-09-08 09:20:53 +08:00
v.logger.Debugf("eading file: %s", filename)
file, err := afero.ReadFile(v.fs, filename)
if err != nil {
return err
2014-04-04 17:21:59 -04:00
}
config := make(map[string]interface{})
err = v.unmarshalReader(bytes.NewReader(file), config)
if err != nil {
return err
}
v.config = config
return nil
2014-04-04 17:21:59 -04:00
}
// MergeInConfig merges a new configuration with an existing config.
func MergeInConfig() error { return v.MergeInConfig() }
2020-09-08 09:20:53 +08:00
// MergeInConfig merges a new configuration with an existing config.
func (v *Viper) MergeInConfig() error {
2020-09-08 09:20:53 +08:00
v.logger.Infof("Attempting to merge in config file")
filename, err := v.getConfigFile()
if err != nil {
return err
}
if !stringInSlice(v.getConfigType(), SupportedExts) {
return UnsupportedConfigError(v.getConfigType())
}
file, err := afero.ReadFile(v.fs, filename)
if err != nil {
return err
}
return v.MergeConfig(bytes.NewReader(file))
}
// ReadConfig will read a configuration file, setting existing keys to nil if the
// key does not exist in the file.
2015-05-14 17:40:59 +08:00
func ReadConfig(in io.Reader) error { return v.ReadConfig(in) }
2020-09-08 09:20:53 +08:00
// ReadConfig will read a configuration file, setting existing keys to nil if the
// key does not exist in the file.
2015-05-14 17:40:59 +08:00
func (v *Viper) ReadConfig(in io.Reader) error {
2015-05-08 17:13:33 +08:00
v.config = make(map[string]interface{})
return v.unmarshalReader(in, v.config)
2015-05-08 17:13:33 +08:00
}
// MergeConfig merges a new configuration with an existing config.
func MergeConfig(in io.Reader) error { return v.MergeConfig(in) }
2020-09-08 09:20:53 +08:00
// MergeConfig merges a new configuration with an existing config.
func (v *Viper) MergeConfig(in io.Reader) error {
if v.config == nil {
v.config = make(map[string]interface{})
}
cfg := make(map[string]interface{})
if err := v.unmarshalReader(in, cfg); err != nil {
return err
}
mergeMaps(cfg, v.config, nil)
return nil
}
2020-09-08 10:34:59 +08:00
// 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) }
// MergeConfigMap merges the configuration from the map given with an existing config.
// Note that the map given may be modified.
func (v *Viper) MergeConfigMap(cfg map[string]interface{}) error {
if v.config == nil {
v.config = make(map[string]interface{})
}
insensitiviseMap(cfg)
mergeMaps(cfg, v.config, nil)
return nil
}
// WriteConfig writes the current configuration to a file.
func WriteConfig() error { return v.WriteConfig() }
2020-09-08 09:20:53 +08:00
// WriteConfig writes the current configuration to a file.
func (v *Viper) WriteConfig() error {
filename, err := v.getConfigFile()
if err != nil {
return err
}
return v.writeConfig(filename, true)
}
// SafeWriteConfig writes current configuration to file only if the file does not exist.
func SafeWriteConfig() error { return v.SafeWriteConfig() }
2020-09-08 09:20:53 +08:00
// SafeWriteConfig writes current configuration to file only if the file does not exist.
func (v *Viper) SafeWriteConfig() error {
filename, err := v.getConfigFile()
if err != nil {
return err
}
return v.writeConfig(filename, false)
}
// WriteConfigAs writes current configuration to a given filename.
func WriteConfigAs(filename string) error { return v.WriteConfigAs(filename) }
2020-09-08 09:20:53 +08:00
// WriteConfigAs writes current configuration to a given 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) }
2020-09-08 09:20:53 +08:00
// SafeWriteConfigAs writes current configuration to a given filename if it does not exist.
func (v *Viper) SafeWriteConfigAs(filename string) error {
2020-09-08 10:34:59 +08:00
alreadyExists, err := afero.Exists(v.fs, filename)
if alreadyExists && err == nil {
return ConfigFileAlreadyExistsError(filename)
}
return v.writeConfig(filename, false)
}
func writeConfig(filename string, force bool) error { return v.writeConfig(filename, force) }
func (v *Viper) writeConfig(filename string, force bool) error {
2020-09-08 09:20:53 +08:00
v.logger.Infof("Attempting to write in config file")
2020-09-08 10:34:59 +08:00
var configType string
ext := filepath.Ext(filename)
2020-09-08 10:34:59 +08:00
if ext != "" {
configType = ext[1:]
} else {
configType = SupportedExts[0]
}
2020-09-08 10:34:59 +08:00
if configType == "" {
return fmt.Errorf("config type could not be determined for %s", filename)
}
if !stringInSlice(configType, SupportedExts) {
return UnsupportedConfigError(configType)
}
if v.config == nil {
v.config = make(map[string]interface{})
}
2020-09-08 10:34:59 +08:00
flags := os.O_CREATE | os.O_TRUNC | os.O_WRONLY
if !force {
flags |= os.O_EXCL
}
2020-09-08 10:34:59 +08:00
f, err := v.fs.OpenFile(filename, flags, v.configPermissions)
if err != nil {
return err
}
2020-09-08 10:34:59 +08:00
defer f.Close()
if err := v.marshalWriter(f, configType); err != nil {
return err
}
return f.Sync()
}
// Unmarshal a Reader into a map.
// Should probably be an unexported function.
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 "json":
if err := json.Unmarshal(buf.Bytes(), &c); err != nil {
return ConfigParseError{err}
}
}
insensitiviseMap(c)
return nil
}
// Marshal a map into Writer.
func marshalWriter(f afero.File, configType string) error {
return v.marshalWriter(f, configType)
}
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))
if err != nil {
return ConfigMarshalError{err}
}
}
return nil
}
func keyExists(k string, m map[string]interface{}) string {
lk := strings.ToLower(k)
for mk := range m {
lmk := strings.ToLower(mk)
if lmk == lk {
return mk
}
}
return ""
}
func castToMapStringInterface(
src map[interface{}]interface{}) map[string]interface{} {
tgt := map[string]interface{}{}
for k, v := range src {
tgt[fmt.Sprintf("%v", k)] = v
}
return tgt
}
func castMapStringToMapInterface(src map[string]string) map[string]interface{} {
tgt := map[string]interface{}{}
for k, v := range src {
tgt[k] = v
}
return tgt
}
func castMapFlagToMapInterface(src map[string]FlagValue) map[string]interface{} {
tgt := map[string]interface{}{}
for k, v := range src {
tgt[k] = v
}
return tgt
}
// mergeMaps merges two maps. The `itgt` parameter is for handling go-yaml's
// insistence on parsing nested structures as `map[interface{}]interface{}`
// instead of using a `string` as the key for nest structures beyond one level
// 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{}) {
for sk, sv := range src {
tk := keyExists(sk, tgt)
if tk == "" {
2020-09-08 09:20:53 +08:00
v.logger.Debugf("tk=\"\", tgt[%s]=%v", sk, sv)
tgt[sk] = sv
if itgt != nil {
itgt[sk] = sv
}
continue
}
tv, ok := tgt[tk]
if !ok {
2020-09-08 09:20:53 +08:00
v.logger.Debugf("tgt[%s] != ok, tgt[%s]=%v", tk, sk, sv)
tgt[sk] = sv
if itgt != nil {
itgt[sk] = sv
}
continue
}
svType := reflect.TypeOf(sv)
tvType := reflect.TypeOf(tv)
if svType != tvType {
2020-09-08 09:20:53 +08:00
v.logger.Errorf("svType != tvType; key=%s, st=%v, tt=%v, sv=%v, tv=%v", sk, svType, tvType, sv, tv)
continue
}
2020-09-08 09:20:53 +08:00
v.logger.Debugf("processing key=%s, st=%v, tt=%v, sv=%v, tv=%v", sk, svType, tvType, sv, tv)
switch ttv := tv.(type) {
case map[interface{}]interface{}:
2020-09-08 09:20:53 +08:00
v.logger.Debugf("merging maps (must convert)")
tsv := sv.(map[interface{}]interface{})
ssv := castToMapStringInterface(tsv)
stv := castToMapStringInterface(ttv)
mergeMaps(ssv, stv, ttv)
case map[string]interface{}:
2020-09-08 09:20:53 +08:00
v.logger.Debugf("merging maps")
mergeMaps(sv.(map[string]interface{}), ttv, nil)
default:
2020-09-08 09:20:53 +08:00
v.logger.Debugf("setting value")
tgt[tk] = sv
if itgt != nil {
itgt[tk] = sv
}
}
}
}
func (v *Viper) insensitiviseMaps() {
insensitiviseMap(v.config)
insensitiviseMap(v.defaults)
insensitiviseMap(v.override)
insensitiviseMap(v.kvstore)
2014-10-24 15:38:01 -04:00
}
// 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() }
2020-09-08 09:20:53 +08:00
// AllKeys returns all keys holding a value, regardless of where they are set.
// Nested keys are returned with a v.keyDelim (= ".") separator
func (v *Viper) AllKeys() []string {
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.flattenAndMergeMap(m, v.config, "")
m = v.flattenAndMergeMap(m, v.kvstore, "")
m = v.flattenAndMergeMap(m, v.defaults, "")
// convert set of paths to list
a := []string{}
for x := range m {
a = append(a, x)
}
return a
}
// flattenAndMergeMap recursively flattens the given map into a map[string]bool
// of key paths (used as a set, easier to manipulate than a []string):
2022-09-16 21:35:05 +08:00
// - each path is merged into a single key string, delimited with v.keyDelim (= ".")
// - if a path is shadowed by an earlier value in the initial shadow map,
// it is skipped.
//
// The resulting set of paths is merged to the given shadow set at the same time.
func (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[string]interface{}, prefix string) map[string]bool {
if shadow != nil && prefix != "" && shadow[prefix] {
// prefix is shadowed => nothing more to flatten
return shadow
2014-10-24 15:38:01 -04:00
}
if shadow == nil {
shadow = make(map[string]bool)
}
var m2 map[string]interface{}
if prefix != "" {
prefix += v.keyDelim
}
for k, val := range m {
fullKey := prefix + k
switch val.(type) {
case map[string]interface{}:
m2 = val.(map[string]interface{})
case map[interface{}]interface{}:
m2 = cast.ToStringMap(val)
default:
// immediate value
shadow[strings.ToLower(fullKey)] = true
continue
}
// recursively merge to shadow map
shadow = v.flattenAndMergeMap(shadow, m2, fullKey)
}
return shadow
}
// mergeFlatMap merges the given maps, excluding values of the second map
// shadowed by values from the first map.
func (v *Viper) mergeFlatMap(shadow map[string]bool, m map[string]interface{}) map[string]bool {
// scan keys
outer:
2020-09-08 09:20:53 +08:00
for k := range m {
path := strings.Split(k, v.keyDelim)
// scan intermediate paths
var parentKey string
for i := 1; i < len(path); i++ {
parentKey = strings.Join(path[0:i], v.keyDelim)
if shadow[parentKey] {
// path is shadowed, continue
continue outer
}
}
// add key
shadow[strings.ToLower(k)] = true
}
return shadow
}
// AllSettings merges all settings and returns them as a map[string]interface{}.
func AllSettings() map[string]interface{} { return v.AllSettings() }
2020-09-08 09:20:53 +08:00
// AllSettings merges all settings and returns them as a map[string]interface{}.
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
for _, k := range v.AllKeys() {
value := v.Get(k)
if value == nil {
// should not happen, since AllKeys() returns only keys holding a value,
// check just in case anything changes
continue
}
path := strings.Split(k, v.keyDelim)
lastKey := strings.ToLower(path[len(path)-1])
deepestMap := deepSearch(m, path[0:len(path)-1])
// set innermost value
deepestMap[lastKey] = value
}
return m
}
// SetFs sets the filesystem to use to read configuration.
func SetFs(fs afero.Fs) { v.SetFs(fs) }
2020-09-08 09:20:53 +08:00
// SetFs sets the filesystem to use to read configuration.
func (v *Viper) SetFs(fs afero.Fs) {
v.fs = fs
}
// SetConfigName sets name for the config file.
2014-07-11 10:42:07 -04:00
// Does not include extension.
func SetConfigName(in string) { v.SetConfigName(in) }
2020-09-08 09:20:53 +08:00
// SetConfigName sets name for the config file.
// Does not include extension.
func (v *Viper) SetConfigName(in string) {
2014-04-04 17:21:59 -04:00
if in != "" {
v.configName = in
v.configFile = ""
2014-04-04 17:21:59 -04:00
}
}
2020-09-08 10:34:59 +08:00
// SetConfigPermissions sets the permissions for the config file.
func SetConfigPermissions(perm os.FileMode) { v.SetConfigPermissions(perm) }
// SetConfigPermissions sets the permissions for the config file.
func (v *Viper) SetConfigPermissions(perm os.FileMode) {
v.configPermissions = perm.Perm()
}
func (v *Viper) getConfigType() string {
2014-04-04 17:21:59 -04:00
cf, err := v.getConfigFile()
if err != nil {
2020-09-08 10:34:59 +08:00
return "json"
}
ext := filepath.Ext(cf)
2014-04-04 17:21:59 -04:00
if len(ext) > 1 {
return ext[1:]
}
2020-09-08 10:34:59 +08:00
return "json"
2014-04-04 17:21:59 -04:00
}
func (v *Viper) getConfigFile() (string, error) {
2018-03-19 19:12:24 +01:00
if v.configFile == "" {
cf, err := v.findConfigFile()
if err != nil {
return "", err
}
v.configFile = cf
2014-04-04 17:21:59 -04:00
}
2018-03-19 19:12:24 +01:00
return v.configFile, nil
2014-04-04 17:21:59 -04:00
}
func (v *Viper) searchInPath(in string) (filename string) {
2020-09-08 09:20:53 +08:00
v.logger.Debugf("Searching for config in %s", in)
2014-04-04 17:21:59 -04:00
for _, ext := range SupportedExts {
2020-09-08 09:20:53 +08:00
v.logger.Debugf("Checking for %s", filepath.Join(in, v.configName+"."+ext))
if b, _ := exists(v.fs, filepath.Join(in, v.configName+"."+ext)); b {
2020-09-08 09:20:53 +08:00
v.logger.Debugf("Found: %s", filepath.Join(in, v.configName+"."+ext))
return filepath.Join(in, v.configName+"."+ext)
2014-04-04 17:21:59 -04:00
}
}
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) {
2020-09-08 09:20:53 +08:00
v.logger.Infof("Searching for config in %s", v.configPaths)
2014-04-04 17:21:59 -04:00
for _, cp := range v.configPaths {
file := v.searchInPath(cp)
2014-04-04 17:21:59 -04:00
if file != "" {
return file, nil
}
}
2015-08-01 19:37:27 -05:00
return "", ConfigFileNotFoundError{v.configName, fmt.Sprintf("%s", v.configPaths)}
}
// Debug prints all configuration registries for debugging
// purposes.
func Debug() { v.Debug() }
2020-09-08 09:20:53 +08:00
// Debug prints all configuration registries for debugging
// purposes.
func (v *Viper) Debug() {
fmt.Printf("Aliases:\n%#v\n", v.aliases)
fmt.Printf("Override:\n%#v\n", v.override)
fmt.Printf("PFlags:\n%#v\n", v.pflags)
fmt.Printf("Env:\n%#v\n", v.env)
fmt.Printf("Key/Value Store:\n%#v\n", v.kvstore)
fmt.Printf("Config:\n%#v\n", v.config)
fmt.Printf("Defaults:\n%#v\n", v.defaults)
2014-04-04 17:21:59 -04:00
}
2020-09-08 09:20:53 +08:00
// Logger is implemented by any logging system that is used for standard logs.
type Logger interface {
Errorf(string, ...interface{})
Warningf(string, ...interface{})
Infof(string, ...interface{})
Debugf(string, ...interface{})
}
// Errorf logs an ERROR log message to the logger specified in opts or to the
// global logger if no logger is specified in opts.
func (v *Viper) Errorf(format string, vIn ...interface{}) {
if v.logger == nil {
return
}
v.logger.Errorf(format, vIn...)
}
// Infof logs an INFO message to the logger specified in opts.
func (v *Viper) Infof(format string, vIn ...interface{}) {
if v.logger == nil {
return
}
v.logger.Infof(format, vIn...)
}
// Warningf logs a WARNING message to the logger specified in opts.
func (v *Viper) Warningf(format string, vIn ...interface{}) {
if v.logger == nil {
return
}
v.logger.Warningf(format, vIn...)
}
// Debugf logs a DEBUG message to the logger specified in opts.
func (v *Viper) Debugf(format string, vIn ...interface{}) {
if v.logger == nil {
return
}
v.logger.Debugf(format, vIn...)
}
type loggingLevel int
const (
// DEBUG debug log level
DEBUG loggingLevel = iota
// INFO log level
INFO
// WARNING log level
WARNING
// ERROR log level
ERROR
)
// DefaultLog call inline log obj
type DefaultLog struct {
*log.Logger
level loggingLevel
}
// DefaultLogger set default loagger call inline log
func DefaultLogger(level loggingLevel) *DefaultLog {
return &DefaultLog{Logger: log.New(os.Stderr, "viper ", log.LstdFlags), level: level}
}
// Errorf for DefaultLog
func (l *DefaultLog) Errorf(f string, v ...interface{}) {
if l.level <= ERROR {
l.Printf("ERROR: "+f, v...)
}
}
// Warningf for DefaultLog
func (l *DefaultLog) Warningf(f string, v ...interface{}) {
if l.level <= WARNING {
l.Printf("WARNING: "+f, v...)
}
}
// Infof for DefaultLog
func (l *DefaultLog) Infof(f string, v ...interface{}) {
if l.level <= INFO {
l.Printf("INFO: "+f, v...)
}
}
// Debugf for DefaultLog
func (l *DefaultLog) Debugf(f string, v ...interface{}) {
if l.level <= DEBUG {
l.Printf("DEBUG: "+f, v...)
}
}