2014-04-05 01: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.
2019-08-17 00:15:10 +03:00
// Viper is an application configuration system.
2014-09-28 01:03:00 +04:00
// It believes that applications can be configured a variety of ways
2014-10-24 23:38:01 +04:00
// via flags, ENVIRONMENT variables, configuration files retrieved
// from the file system, or a remote key/value store.
2014-09-28 01:03:00 +04:00
// Each item takes precedence over the item below it:
2015-04-02 00:08:42 +03:00
// overrides
2014-09-28 01:03:00 +04:00
// flag
// env
// config
2014-10-24 23:38:01 +04:00
// key/value store
2014-09-28 01:03:00 +04:00
// default
2014-04-05 01:21:59 +04:00
package viper
import (
"bytes"
2017-04-17 11:08:15 +03:00
"encoding/csv"
2019-12-04 04:14:08 +03:00
"errors"
2014-04-05 01:21:59 +04:00
"fmt"
"io"
"os"
"path/filepath"
2014-10-24 23:38:01 +04:00
"reflect"
2020-10-04 21:07:34 +03:00
"strconv"
2014-04-05 09:19:39 +04:00
"strings"
2018-01-03 12:37:18 +03:00
"sync"
2014-04-05 01:21:59 +04:00
"time"
2016-04-20 16:51:22 +03:00
"github.com/fsnotify/fsnotify"
2014-06-27 01:58:55 +04:00
"github.com/mitchellh/mapstructure"
2023-09-12 01:03:41 +03:00
slog "github.com/sagikazarmark/slog-shim"
2016-08-05 10:24:49 +03:00
"github.com/spf13/afero"
2015-07-30 23:44:12 +03:00
"github.com/spf13/cast"
2014-06-27 20:29:37 +04:00
"github.com/spf13/pflag"
2020-09-23 18:46:17 +03:00
"github.com/spf13/viper/internal/encoding"
2021-07-20 02:46:45 +03:00
"github.com/spf13/viper/internal/encoding/dotenv"
2020-09-23 18:46:17 +03:00
"github.com/spf13/viper/internal/encoding/hcl"
2021-07-16 04:14:41 +03:00
"github.com/spf13/viper/internal/encoding/ini"
2021-07-16 04:51:02 +03:00
"github.com/spf13/viper/internal/encoding/javaproperties"
2020-09-23 18:46:17 +03:00
"github.com/spf13/viper/internal/encoding/json"
"github.com/spf13/viper/internal/encoding/toml"
"github.com/spf13/viper/internal/encoding/yaml"
2023-12-15 15:31:38 +03:00
"github.com/spf13/viper/internal/features"
2014-04-05 01:21:59 +04:00
)
2017-12-07 07:26:31 +03: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 ( ) )
}
2015-02-17 17:22:37 +03:00
var v * Viper
2014-12-05 05:55:51 +03:00
2017-03-15 16:43:09 +03:00
type RemoteResponse struct {
Value [ ] byte
Error error
}
2014-12-05 05:55:51 +03:00
func init ( ) {
v = New ( )
}
2015-05-30 22:28:33 +03:00
type remoteConfigFactory interface {
Get ( rp RemoteProvider ) ( io . Reader , error )
Watch ( rp RemoteProvider ) ( io . Reader , error )
2017-06-19 13:35:39 +03:00
WatchChannel ( rp RemoteProvider ) ( <- chan * RemoteResponse , chan bool )
2015-05-30 22:28:33 +03:00
}
2023-10-09 17:52:53 +03:00
// RemoteConfig is optional, see the remote package.
2015-05-30 22:28:33 +03:00
var RemoteConfig remoteConfigFactory
2016-09-20 11:17:41 +03:00
// UnsupportedConfigError denotes encountering an unsupported
2015-04-02 00:08:42 +03:00
// configuration filetype.
2014-12-05 05:55:51 +03:00
type UnsupportedConfigError string
2016-09-20 11:17:41 +03:00
// Error returns the formatted configuration error.
2014-12-05 05:55:51 +03:00
func ( str UnsupportedConfigError ) Error ( ) string {
return fmt . Sprintf ( "Unsupported Config Type %q" , string ( str ) )
}
2016-09-20 11:17:41 +03:00
// UnsupportedRemoteProviderError denotes encountering an unsupported remote
2017-07-23 08:39:01 +03:00
// provider. Currently only etcd and Consul are supported.
2014-12-05 05:55:51 +03:00
type UnsupportedRemoteProviderError string
2016-09-20 11:17:41 +03:00
// Error returns the formatted remote provider error.
2014-12-05 05:55:51 +03:00
func ( str UnsupportedRemoteProviderError ) Error ( ) string {
return fmt . Sprintf ( "Unsupported Remote Provider Type %q" , string ( str ) )
}
2016-09-20 11:17:41 +03:00
// RemoteConfigError denotes encountering an error while trying to
2015-04-02 00:08:42 +03:00
// pull the configuration from the remote provider.
2014-12-05 05:55:51 +03:00
type RemoteConfigError string
2023-10-09 17:52:53 +03:00
// Error returns the formatted remote provider error.
2014-12-05 05:55:51 +03:00
func ( rce RemoteConfigError ) Error ( ) string {
return fmt . Sprintf ( "Remote Configurations Error: %s" , string ( rce ) )
}
2016-09-20 11:17:41 +03:00
// ConfigFileNotFoundError denotes failing to find configuration file.
2015-08-02 03:37:27 +03:00
type ConfigFileNotFoundError struct {
name , locations string
}
2016-09-20 11:17:41 +03:00
// Error returns the formatted configuration error.
2015-08-02 03:37:27 +03:00
func ( fnfe ConfigFileNotFoundError ) Error ( ) string {
return fmt . Sprintf ( "Config File %q Not Found in %q" , fnfe . name , fnfe . locations )
}
2019-09-13 17:29:19 +03: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 ) )
}
2018-06-28 12:55:33 +03:00
// A DecoderConfigOption can be passed to viper.Unmarshal to configure
2023-10-09 17:52:53 +03:00
// mapstructure.DecoderConfig options.
2018-06-28 12:55:33 +03:00
type DecoderConfigOption func ( * mapstructure . DecoderConfig )
// DecodeHook returns a DecoderConfigOption which overrides the default
// DecoderConfig.DecodeHook value, the default is:
//
2022-09-03 19:05:45 +03:00
// mapstructure.ComposeDecodeHookFunc(
// mapstructure.StringToTimeDurationHookFunc(),
// mapstructure.StringToSliceHookFunc(","),
// )
2018-06-28 12:55:33 +03:00
func DecodeHook ( hook mapstructure . DecodeHookFunc ) DecoderConfigOption {
return func ( c * mapstructure . DecoderConfig ) {
c . DecodeHook = hook
}
}
2015-04-02 00:08:42 +03:00
// 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-03 19:05:45 +03:00
// Defaults : {
// "secret": "",
// "user": "default",
// "endpoint": "https://localhost"
// }
// Config : {
// "user": "root"
// "secret": "defaultsecret"
// }
// Env : {
// "secret": "somesecretkey"
// }
2015-04-02 00:08:42 +03:00
//
// The resulting config will have the following values:
//
// {
// "secret": "somesecretkey",
// "user": "root",
// "endpoint": "https://localhost"
// }
2021-04-13 16:12:40 +03:00
//
// Note: Vipers are not safe for concurrent Get() and Set() operations.
2015-02-17 17:22:37 +03:00
type Viper struct {
2015-04-26 22:02:19 +03:00
// Delimiter that separates a list of keys
// used to access a nested value in one go
keyDelim string
2014-12-05 05:55:51 +03:00
// A set of paths to look for the config file in
configPaths [ ] string
2016-08-05 10:45:58 +03:00
// The filesystem to read config from.
fs afero . Fs
2014-12-05 05:55:51 +03:00
// A set of remote providers to search for the configuration
2015-05-30 22:28:33 +03:00
remoteProviders [ ] * defaultRemoteProvider
2014-12-05 05:55:51 +03:00
// Name of file to look for inside the path
2019-02-22 21:54:48 +03:00
configName string
configFile string
configType string
configPermissions os . FileMode
envPrefix string
2014-12-05 05:55:51 +03:00
2021-01-26 22:18:05 +03:00
// Specific commands for ini parsing
iniLoadOptions ini . LoadOptions
2014-12-23 06:47:25 +03:00
automaticEnvApplied bool
2019-12-06 16:11:31 +03:00
envKeyReplacer StringReplacer
2018-11-07 00:53:21 +03:00
allowEmptyEnv bool
2014-12-23 06:47:25 +03:00
2020-12-24 09:04:37 +03:00
parents [ ] string
2023-09-26 17:59:38 +03:00
config map [ string ] any
override map [ string ] any
defaults map [ string ] any
kvstore map [ string ] any
2015-12-10 21:14:17 +03:00
pflags map [ string ] FlagValue
2020-09-10 13:08:26 +03:00
env map [ string ] [ ] string
[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 18:54:20 +03:00
aliases map [ string ] string
typeByDefValue bool
2015-11-10 07:22:04 +03:00
onConfigChange func ( fsnotify . Event )
2021-12-09 20:36:49 +03:00
2023-09-12 01:03:41 +03:00
logger * slog . Logger
2021-07-16 04:09:43 +03:00
// TODO: should probably be protected with a mutex
encoderRegistry * encoding . EncoderRegistry
decoderRegistry * encoding . DecoderRegistry
2014-12-05 05:55:51 +03:00
}
2016-09-20 11:17:41 +03:00
// New returns an initialized Viper instance.
2015-02-17 17:22:37 +03:00
func New ( ) * Viper {
v := new ( Viper )
2015-04-26 22:02:19 +03:00
v . keyDelim = "."
2014-12-05 05:55:51 +03:00
v . configName = "config"
2021-09-15 22:12:02 +03:00
v . configPermissions = os . FileMode ( 0 o644 )
2016-08-05 10:45:58 +03:00
v . fs = afero . NewOsFs ( )
2023-09-26 17:59:38 +03:00
v . config = make ( map [ string ] any )
2020-12-24 09:04:37 +03:00
v . parents = [ ] string { }
2023-09-26 17:59:38 +03:00
v . override = make ( map [ string ] any )
v . defaults = make ( map [ string ] any )
v . kvstore = make ( map [ string ] any )
2015-12-10 21:14:17 +03:00
v . pflags = make ( map [ string ] FlagValue )
2020-09-10 13:08:26 +03:00
v . env = make ( map [ string ] [ ] string )
2014-12-05 05:55:51 +03:00
v . aliases = make ( map [ string ] string )
[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 18:54:20 +03:00
v . typeByDefValue = false
2023-09-12 01:03:41 +03:00
v . logger = slog . New ( & discardHandler { } )
2014-12-05 05:55:51 +03:00
2021-07-16 04:09:43 +03:00
v . resetEncoding ( )
2014-12-05 05:55:51 +03:00
return v
}
2019-11-08 16:23:20 +03: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
} )
}
2019-12-06 16:11:31 +03:00
// 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
} )
}
2019-11-08 16:23:20 +03:00
// NewWithOptions creates a new Viper instance.
func NewWithOptions ( opts ... Option ) * Viper {
v := New ( )
for _ , opt := range opts {
opt . apply ( v )
}
2021-07-16 04:09:43 +03:00
v . resetEncoding ( )
2019-11-08 16:23:20 +03:00
return v
}
2019-07-13 13:04:26 +03:00
// Reset is intended for testing, will reset all to default settings.
2015-02-19 18:39:44 +03:00
// In the public interface for the viper package so applications
// can use it in their testing as well.
func Reset ( ) {
v = New ( )
2020-05-13 20:15:28 +03:00
SupportedExts = [ ] string { "json" , "toml" , "yaml" , "yml" , "properties" , "props" , "prop" , "hcl" , "tfvars" , "dotenv" , "env" , "ini" }
2023-07-21 22:10:15 +03:00
SupportedRemoteProviders = [ ] string { "etcd" , "etcd3" , "consul" , "firestore" , "nats" }
2015-02-19 18:39:44 +03:00
}
2023-10-09 17:52:53 +03:00
// TODO: make this lazy initialization instead.
2021-07-16 04:09:43 +03:00
func ( v * Viper ) resetEncoding ( ) {
encoderRegistry := encoding . NewEncoderRegistry ( )
decoderRegistry := encoding . NewDecoderRegistry ( )
{
codec := yaml . Codec { }
encoderRegistry . RegisterEncoder ( "yaml" , codec )
decoderRegistry . RegisterDecoder ( "yaml" , codec )
encoderRegistry . RegisterEncoder ( "yml" , codec )
decoderRegistry . RegisterDecoder ( "yml" , codec )
}
{
codec := json . Codec { }
encoderRegistry . RegisterEncoder ( "json" , codec )
decoderRegistry . RegisterDecoder ( "json" , codec )
}
{
codec := toml . Codec { }
encoderRegistry . RegisterEncoder ( "toml" , codec )
decoderRegistry . RegisterDecoder ( "toml" , codec )
}
{
codec := hcl . Codec { }
encoderRegistry . RegisterEncoder ( "hcl" , codec )
decoderRegistry . RegisterDecoder ( "hcl" , codec )
encoderRegistry . RegisterEncoder ( "tfvars" , codec )
decoderRegistry . RegisterDecoder ( "tfvars" , codec )
}
2021-07-16 04:14:41 +03:00
{
2023-12-01 22:51:58 +03:00
codec := ini . Codec {
2021-07-16 04:14:41 +03:00
KeyDelimiter : v . keyDelim ,
LoadOptions : v . iniLoadOptions ,
}
encoderRegistry . RegisterEncoder ( "ini" , codec )
decoderRegistry . RegisterDecoder ( "ini" , codec )
}
2021-07-16 04:51:02 +03:00
{
codec := & javaproperties . Codec {
KeyDelimiter : v . keyDelim ,
}
encoderRegistry . RegisterEncoder ( "properties" , codec )
decoderRegistry . RegisterDecoder ( "properties" , codec )
encoderRegistry . RegisterEncoder ( "props" , codec )
decoderRegistry . RegisterDecoder ( "props" , codec )
encoderRegistry . RegisterEncoder ( "prop" , codec )
decoderRegistry . RegisterDecoder ( "prop" , codec )
}
2021-07-20 02:46:45 +03:00
{
codec := & dotenv . Codec { }
encoderRegistry . RegisterEncoder ( "dotenv" , codec )
decoderRegistry . RegisterDecoder ( "dotenv" , codec )
encoderRegistry . RegisterEncoder ( "env" , codec )
decoderRegistry . RegisterDecoder ( "env" , codec )
}
2021-07-16 04:09:43 +03:00
v . encoderRegistry = encoderRegistry
v . decoderRegistry = decoderRegistry
}
2015-05-30 22:28:33 +03:00
type defaultRemoteProvider struct {
2014-10-24 23:38:01 +04:00
provider string
endpoint string
path string
secretKeyring string
}
2015-05-30 22:28:33 +03:00
func ( rp defaultRemoteProvider ) Provider ( ) string {
return rp . provider
}
func ( rp defaultRemoteProvider ) Endpoint ( ) string {
return rp . endpoint
}
func ( rp defaultRemoteProvider ) Path ( ) string {
return rp . path
}
func ( rp defaultRemoteProvider ) SecretKeyring ( ) string {
return rp . secretKeyring
}
// RemoteProvider stores the configuration necessary
// to connect to a remote key/value store.
// Optional secretKeyring to unencrypt encrypted values
// can be provided.
type RemoteProvider interface {
Provider ( ) string
Endpoint ( ) string
Path ( ) string
SecretKeyring ( ) string
}
2016-09-20 11:17:41 +03:00
// SupportedExts are universally supported extensions.
2020-05-13 20:15:28 +03:00
var SupportedExts = [ ] string { "json" , "toml" , "yaml" , "yml" , "properties" , "props" , "prop" , "hcl" , "tfvars" , "dotenv" , "env" , "ini" }
2014-04-05 01:21:59 +04:00
2016-09-20 11:17:41 +03:00
// SupportedRemoteProviders are universally supported remote providers.
2023-07-21 22:10:15 +03:00
var SupportedRemoteProviders = [ ] string { "etcd" , "etcd3" , "consul" , "firestore" , "nats" }
2014-04-05 01:21:59 +04:00
2023-01-19 18:38:18 +03:00
// OnConfigChange sets the event handler that is called when a config file changes.
2015-11-10 07:22:04 +03:00
func OnConfigChange ( run func ( in fsnotify . Event ) ) { v . OnConfigChange ( run ) }
2023-01-19 18:38:18 +03:00
// OnConfigChange sets the event handler that is called when a config file changes.
2015-11-10 07:22:04 +03:00
func ( v * Viper ) OnConfigChange ( run func ( in fsnotify . Event ) ) {
v . onConfigChange = run
}
2023-01-19 18:38:18 +03:00
// WatchConfig starts watching a config file for changes.
2015-11-10 07:22:04 +03:00
func WatchConfig ( ) { v . WatchConfig ( ) }
2018-07-13 11:30:23 +03:00
2023-01-19 18:38:18 +03:00
// WatchConfig starts watching a config file for changes.
2015-11-10 07:22:04 +03:00
func ( v * Viper ) WatchConfig ( ) {
2018-05-24 11:09:29 +03:00
initWG := sync . WaitGroup { }
initWG . Add ( 1 )
2015-11-10 07:22:04 +03:00
go func ( ) {
2023-11-29 19:59:13 +03:00
watcher , err := fsnotify . NewWatcher ( )
2023-01-20 13:30:52 +03:00
if err != nil {
2023-05-29 16:31:29 +03:00
v . logger . Error ( fmt . Sprintf ( "failed to create watcher: %s" , err ) )
2023-01-20 13:30:52 +03:00
os . Exit ( 1 )
2015-11-10 07:22:04 +03:00
}
defer watcher . Close ( )
2016-01-11 18:07:23 +03:00
// we have to watch the entire directory to pick up renames/atomic saves in a cross-platform way
2016-10-13 14:33:30 +03:00
filename , err := v . getConfigFile ( )
if err != nil {
2023-05-29 16:31:29 +03:00
v . logger . Error ( fmt . Sprintf ( "get config file: %s" , err ) )
2019-01-03 17:31:09 +03:00
initWG . Done ( )
2016-10-13 14:33:30 +03:00
return
}
configFile := filepath . Clean ( filename )
2016-01-11 18:07:23 +03:00
configDir , _ := filepath . Split ( configFile )
2018-01-03 12:37:18 +03:00
realConfigFile , _ := filepath . EvalSymlinks ( filename )
2016-01-11 18:07:23 +03:00
2018-05-24 11:09:29 +03:00
eventsWG := sync . WaitGroup { }
eventsWG . Add ( 1 )
2015-11-10 07:22:04 +03:00
go func ( ) {
for {
select {
2018-05-24 11:09:29 +03:00
case event , ok := <- watcher . Events :
if ! ok { // 'Events' channel is closed
eventsWG . Done ( )
return
}
2018-01-03 12:37:18 +03:00
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)
if ( filepath . Clean ( event . Name ) == configFile &&
2022-11-03 19:57:32 +03:00
( event . Has ( fsnotify . Write ) || event . Has ( fsnotify . Create ) ) ) ||
2018-01-03 12:37:18 +03:00
( currentConfigFile != "" && currentConfigFile != realConfigFile ) {
realConfigFile = currentConfigFile
err := v . ReadInConfig ( )
2023-01-20 13:30:52 +03:00
if err != nil {
2023-05-29 16:31:29 +03:00
v . logger . Error ( fmt . Sprintf ( "read config file: %s" , err ) )
2018-01-03 12:37:18 +03:00
}
if v . onConfigChange != nil {
2016-01-11 18:07:23 +03:00
v . onConfigChange ( event )
2015-11-10 07:22:04 +03:00
}
2022-11-03 19:57:32 +03:00
} else if filepath . Clean ( event . Name ) == configFile && event . Has ( fsnotify . Remove ) {
2018-05-24 11:09:29 +03:00
eventsWG . Done ( )
return
2015-11-10 07:22:04 +03:00
}
2018-01-03 12:37:18 +03:00
2018-05-24 11:09:29 +03:00
case err , ok := <- watcher . Errors :
2023-01-20 13:30:52 +03:00
if ok { // 'Errors' channel is not closed
2023-05-29 16:31:29 +03:00
v . logger . Error ( fmt . Sprintf ( "watcher error: %s" , err ) )
2018-05-24 11:09:29 +03:00
}
eventsWG . Done ( )
return
2015-11-10 07:22:04 +03:00
}
}
} ( )
2016-01-11 18:07:23 +03:00
watcher . Add ( configDir )
2019-07-20 01:03:44 +03:00
initWG . Done ( ) // done initializing the watch in this go routine, so the parent routine can move on...
2018-05-24 11:09:29 +03:00
eventsWG . Wait ( ) // now, wait for event loop to end in this go-routine...
2015-11-10 07:22:04 +03:00
} ( )
2018-05-24 11:09:29 +03:00
initWG . Wait ( ) // make sure that the go routine above fully ended before returning
2015-11-10 07:22:04 +03:00
}
2017-07-23 08:39:01 +03: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.
2014-12-05 05:55:51 +03:00
func SetConfigFile ( in string ) { v . SetConfigFile ( in ) }
2020-09-11 18:48:38 +03:00
2015-02-17 17:22:37 +03:00
func ( v * Viper ) SetConfigFile ( in string ) {
2014-04-05 01:21:59 +04:00
if in != "" {
2014-12-05 05:55:51 +03:00
v . configFile = in
2014-04-05 01:21:59 +04:00
}
}
2016-09-20 11:17:41 +03:00
// SetEnvPrefix defines a prefix that ENVIRONMENT variables will use.
2017-07-23 08:39:01 +03:00
// E.g. if your prefix is "spf", the env registry will look for env
// variables that start with "SPF_".
2014-12-23 02:31:11 +03:00
func SetEnvPrefix ( in string ) { v . SetEnvPrefix ( in ) }
2020-09-11 18:48:38 +03:00
2015-02-17 17:22:37 +03:00
func ( v * Viper ) SetEnvPrefix ( in string ) {
2014-12-23 02:31:11 +03:00
if in != "" {
v . envPrefix = in
}
}
2023-06-13 11:04:31 +03:00
func GetEnvPrefix ( ) string { return v . GetEnvPrefix ( ) }
func ( v * Viper ) GetEnvPrefix ( ) string {
return v . envPrefix
}
2015-02-17 17:22:37 +03:00
func ( v * Viper ) mergeWithEnvPrefix ( in string ) string {
2014-12-23 02:31:11 +03:00
if v . envPrefix != "" {
2014-12-23 06:47:25 +03:00
return strings . ToUpper ( v . envPrefix + "_" + in )
2014-12-23 02:31:11 +03:00
}
2014-12-23 06:47:25 +03:00
return strings . ToUpper ( in )
2014-12-23 02:31:11 +03:00
}
2018-11-07 00:53:21 +03: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 ) }
2020-09-11 18:48:38 +03:00
2018-11-07 00:53:21 +03:00
func ( v * Viper ) AllowEmptyEnv ( allowEmptyEnv bool ) {
v . allowEmptyEnv = allowEmptyEnv
}
2015-03-06 22:21:17 +03:00
// TODO: should getEnv logic be moved into find(). Can generalize the use of
// rewriting keys many things, Ex: Get('someKey') -> some_key
2017-07-23 08:39:01 +03:00
// (camel case to snake case for JSON keys perhaps)
2015-03-06 22:21:17 +03:00
2016-09-20 11:17:41 +03:00
// getEnv is a wrapper around os.Getenv which replaces characters in the original
2017-07-23 08:09:41 +03:00
// key. This allows env vars which have different keys than the config object
2017-07-23 08:39:01 +03:00
// keys.
2018-11-07 00:53:21 +03:00
func ( v * Viper ) getEnv ( key string ) ( string , bool ) {
2015-03-06 22:21:17 +03:00
if v . envKeyReplacer != nil {
key = v . envKeyReplacer . Replace ( key )
}
2018-11-07 00:53:21 +03:00
val , ok := os . LookupEnv ( key )
return val , ok && ( v . allowEmptyEnv || val != "" )
2015-03-06 22:21:17 +03:00
}
2017-07-23 08:39:01 +03:00
// ConfigFileUsed returns the file used to populate the config registry.
2014-12-05 05:55:51 +03:00
func ConfigFileUsed ( ) string { return v . ConfigFileUsed ( ) }
2015-02-17 17:22:37 +03:00
func ( v * Viper ) ConfigFileUsed ( ) string { return v . configFile }
2014-04-08 07:35:40 +04:00
2016-09-20 11:17:41 +03:00
// AddConfigPath adds a path for Viper to search for the config file in.
2014-07-11 18:42:07 +04:00
// Can be called multiple times to define multiple search paths.
2014-12-05 05:55:51 +03:00
func AddConfigPath ( in string ) { v . AddConfigPath ( in ) }
2020-09-11 18:48:38 +03:00
2015-02-17 17:22:37 +03:00
func ( v * Viper ) AddConfigPath ( in string ) {
2014-04-05 01:21:59 +04:00
if in != "" {
2021-12-09 20:36:49 +03:00
absin := absPathify ( v . logger , in )
v . logger . Info ( "adding path to search paths" , "path" , absin )
2014-12-05 05:55:51 +03:00
if ! stringInSlice ( absin , v . configPaths ) {
v . configPaths = append ( v . configPaths , absin )
2014-04-05 01:21:59 +04:00
}
}
}
2014-10-24 23:38:01 +04:00
// AddRemoteProvider adds a remote configuration source.
// Remote Providers are searched in the order they are added.
2023-07-21 22:10:15 +03:00
// provider is a string value: "etcd", "etcd3", "consul", "firestore" or "nats" are currently supported.
// endpoint is the url. etcd requires http://ip:port, consul requires ip:port, nats requires nats://ip:port
2014-10-24 23:38:01 +04:00
// path is the path in the k/v store to retrieve configuration
// To retrieve a config file called myapp.json from /configs/myapp.json
// you should set path to /configs and set config name (SetConfigName()) to
2023-10-09 17:52:53 +03:00
// "myapp".
2014-10-24 23:38:01 +04:00
func AddRemoteProvider ( provider , endpoint , path string ) error {
2014-12-05 05:55:51 +03:00
return v . AddRemoteProvider ( provider , endpoint , path )
}
2020-09-11 18:48:38 +03:00
2015-02-17 17:22:37 +03:00
func ( v * Viper ) AddRemoteProvider ( provider , endpoint , path string ) error {
2014-10-24 23:38:01 +04:00
if ! stringInSlice ( provider , SupportedRemoteProviders ) {
return UnsupportedRemoteProviderError ( provider )
}
if provider != "" && endpoint != "" {
2021-12-09 20:36:49 +03:00
v . logger . Info ( "adding remote provider" , "provider" , provider , "endpoint" , endpoint )
2015-05-30 22:28:33 +03:00
rp := & defaultRemoteProvider {
2014-10-24 23:38:01 +04:00
endpoint : endpoint ,
provider : provider ,
2014-10-27 19:21:03 +03:00
path : path ,
2014-10-24 23:38:01 +04:00
}
2014-12-05 05:55:51 +03:00
if ! v . providerPathExists ( rp ) {
v . remoteProviders = append ( v . remoteProviders , rp )
2014-10-24 23:38:01 +04:00
}
}
return nil
}
// AddSecureRemoteProvider adds a remote configuration source.
// Secure Remote Providers are searched in the order they are added.
2023-07-21 22:10:15 +03:00
// provider is a string value: "etcd", "etcd3", "consul", "firestore" or "nats" are currently supported.
2014-10-24 23:38:01 +04:00
// endpoint is the url. etcd requires http://ip:port consul requires ip:port
// secretkeyring is the filepath to your openpgp secret keyring. e.g. /etc/secrets/myring.gpg
// path is the path in the k/v store to retrieve configuration
// To retrieve a config file called myapp.json from /configs/myapp.json
// you should set path to /configs and set config name (SetConfigName()) to
2023-10-09 17:52:53 +03:00
// "myapp".
// Secure Remote Providers are implemented with github.com/bketelsen/crypt.
2014-10-27 19:21:03 +03:00
func AddSecureRemoteProvider ( provider , endpoint , path , secretkeyring string ) error {
2014-12-05 05:55:51 +03:00
return v . AddSecureRemoteProvider ( provider , endpoint , path , secretkeyring )
}
2015-02-17 17:22:37 +03:00
func ( v * Viper ) AddSecureRemoteProvider ( provider , endpoint , path , secretkeyring string ) error {
2014-10-24 23:38:01 +04:00
if ! stringInSlice ( provider , SupportedRemoteProviders ) {
return UnsupportedRemoteProviderError ( provider )
}
if provider != "" && endpoint != "" {
2021-12-09 20:36:49 +03:00
v . logger . Info ( "adding remote provider" , "provider" , provider , "endpoint" , endpoint )
2015-05-30 22:28:33 +03:00
rp := & defaultRemoteProvider {
2015-06-22 02:19:00 +03:00
endpoint : endpoint ,
provider : provider ,
path : path ,
secretKeyring : secretkeyring ,
2014-10-24 23:38:01 +04:00
}
2014-12-05 05:55:51 +03:00
if ! v . providerPathExists ( rp ) {
v . remoteProviders = append ( v . remoteProviders , rp )
2014-10-24 23:38:01 +04:00
}
}
return nil
}
2015-05-30 22:28:33 +03:00
func ( v * Viper ) providerPathExists ( p * defaultRemoteProvider ) bool {
2014-12-05 05:55:51 +03:00
for _ , y := range v . remoteProviders {
2014-10-24 23:38:01 +04:00
if reflect . DeepEqual ( y , p ) {
return true
}
}
return false
}
2016-10-08 11:00:18 +03:00
// searchMap recursively searches for a value for path in source map.
// Returns nil if not found.
2016-10-14 12:24:45 +03:00
// Note: This assumes that the path entries and map keys are lower cased.
2023-09-26 17:59:38 +03:00
func ( v * Viper ) searchMap ( source map [ string ] any , path [ ] string ) any {
2015-04-26 22:02:19 +03:00
if len ( path ) == 0 {
return source
}
2016-10-14 12:24:45 +03:00
next , ok := source [ path [ 0 ] ]
if ok {
// Fast path
if len ( path ) == 1 {
return next
2015-10-27 01:52:14 +03:00
}
2016-10-14 12:24:45 +03:00
// Nested case
2023-07-27 21:56:32 +03:00
switch next := next . ( type ) {
2023-09-26 17:59:38 +03:00
case map [ any ] any :
2015-05-27 18:15:02 +03:00
return v . searchMap ( cast . ToStringMap ( next ) , path [ 1 : ] )
2023-09-26 17:59:38 +03:00
case map [ string ] any :
2015-04-26 22:02:19 +03:00
// Type assertion is safe here since it is only reached
// if the type of `next` is the same as the type being asserted
2023-07-27 21:56:32 +03:00
return v . searchMap ( next , path [ 1 : ] )
2015-04-26 22:02:19 +03:00
default :
2016-10-08 11:00:18 +03:00
// got a value but nested key expected, return "nil" for not found
return nil
}
}
return nil
}
2020-10-04 21:07:34 +03:00
// searchIndexableWithPathPrefixes recursively searches for a value for path in source map/slice.
2016-10-08 11:00:18 +03:00
//
2020-10-04 21:07:34 +03:00
// While searchMap() considers each path element as a single map key or slice index, this
2016-10-08 11:00:18 +03:00
// 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).
2016-10-14 12:24:45 +03:00
//
// Note: This assumes that the path entries and map keys are lower cased.
2023-09-26 17:59:38 +03:00
func ( v * Viper ) searchIndexableWithPathPrefixes ( source any , path [ ] string ) any {
2016-10-08 11:00:18 +03:00
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 ) )
2023-09-26 17:59:38 +03:00
var val any
2020-10-04 21:07:34 +03:00
switch sourceIndexable := source . ( type ) {
2023-09-26 17:59:38 +03:00
case [ ] any :
2020-10-04 21:07:34 +03:00
val = v . searchSliceWithPathPrefixes ( sourceIndexable , prefixKey , i , path )
2023-09-26 17:59:38 +03:00
case map [ string ] any :
2020-10-04 21:07:34 +03:00
val = v . searchMapWithPathPrefixes ( sourceIndexable , prefixKey , i , path )
}
if val != nil {
return val
2016-10-08 11:00:18 +03:00
}
}
// not found
return nil
}
2020-10-04 21:07:34 +03:00
// searchSliceWithPathPrefixes searches for a value for path in sourceSlice
//
// This function is part of the searchIndexableWithPathPrefixes recurring search and
// should not be called directly from functions other than searchIndexableWithPathPrefixes.
func ( v * Viper ) searchSliceWithPathPrefixes (
2023-09-26 17:59:38 +03:00
sourceSlice [ ] any ,
2020-10-04 21:07:34 +03:00
prefixKey string ,
pathIndex int ,
path [ ] string ,
2023-09-26 17:59:38 +03:00
) any {
2020-10-04 21:07:34 +03:00
// if the prefixKey is not a number or it is out of bounds of the slice
index , err := strconv . Atoi ( prefixKey )
if err != nil || len ( sourceSlice ) <= index {
return nil
}
next := sourceSlice [ index ]
// Fast path
if pathIndex == len ( path ) {
return next
}
switch n := next . ( type ) {
2023-09-26 17:59:38 +03:00
case map [ any ] any :
2020-10-04 21:07:34 +03:00
return v . searchIndexableWithPathPrefixes ( cast . ToStringMap ( n ) , path [ pathIndex : ] )
2023-09-26 17:59:38 +03:00
case map [ string ] any , [ ] any :
2020-10-04 21:07:34 +03:00
return v . searchIndexableWithPathPrefixes ( n , path [ pathIndex : ] )
default :
// got a value but nested key expected, do nothing and look for next prefix
}
// not found
return nil
}
// searchMapWithPathPrefixes searches for a value for path in sourceMap
//
// This function is part of the searchIndexableWithPathPrefixes recurring search and
// should not be called directly from functions other than searchIndexableWithPathPrefixes.
func ( v * Viper ) searchMapWithPathPrefixes (
2023-09-26 17:59:38 +03:00
sourceMap map [ string ] any ,
2020-10-04 21:07:34 +03:00
prefixKey string ,
pathIndex int ,
path [ ] string ,
2023-09-26 17:59:38 +03:00
) any {
2020-10-04 21:07:34 +03:00
next , ok := sourceMap [ prefixKey ]
if ! ok {
return nil
}
// Fast path
if pathIndex == len ( path ) {
return next
}
// Nested case
switch n := next . ( type ) {
2023-09-26 17:59:38 +03:00
case map [ any ] any :
2020-10-04 21:07:34 +03:00
return v . searchIndexableWithPathPrefixes ( cast . ToStringMap ( n ) , path [ pathIndex : ] )
2023-09-26 17:59:38 +03:00
case map [ string ] any , [ ] any :
2020-10-04 21:07:34 +03:00
return v . searchIndexableWithPathPrefixes ( n , path [ pathIndex : ] )
default :
// got a value but nested key expected, do nothing and look for next prefix
}
// not found
return nil
}
2016-10-08 11:00:18 +03:00
// 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-03 19:05:45 +03:00
//
// "foo.bar.baz" in a lower-priority map
2023-09-26 17:59:38 +03:00
func ( v * Viper ) isPathShadowedInDeepMap ( path [ ] string , m map [ string ] any ) string {
var parentVal any
2016-10-08 11:00:18 +03:00
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 ) {
2023-09-26 17:59:38 +03:00
case map [ any ] any :
2016-10-08 11:00:18 +03:00
continue
2023-09-26 17:59:38 +03:00
case map [ string ] any :
2016-10-08 11:00:18 +03:00
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-03 19:05:45 +03:00
//
// "foo.bar.baz" in a lower-priority map
2023-09-26 17:59:38 +03:00
func ( v * Viper ) isPathShadowedInFlatMap ( path [ ] string , mi any ) string {
2016-10-08 11:00:18 +03:00
// unify input map
2023-12-06 16:41:36 +03:00
var m map [ string ] interface { }
switch miv := mi . ( type ) {
case map [ string ] string :
m = castMapStringToMapInterface ( miv )
case map [ string ] FlagValue :
m = castMapFlagToMapInterface ( miv )
2016-10-08 11:00:18 +03:00
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-03 19:05:45 +03:00
//
// "foo.bar.baz" in a lower-priority map
2016-10-08 11:00:18 +03:00
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 )
2018-11-07 00:53:21 +03:00
if _ , ok := v . getEnv ( v . mergeWithEnvPrefix ( parentKey ) ) ; ok {
2016-10-08 11:00:18 +03:00
return parentKey
2015-04-26 22:02:19 +03:00
}
}
2016-10-08 11:00:18 +03:00
return ""
2015-04-26 22:02:19 +03:00
}
[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 18:54:20 +03: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-03 19:05:45 +03: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 18:54:20 +03:00
//
// Otherwise the Get function would return:
//
2022-09-03 19:05:45 +03: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 18:54:20 +03:00
func SetTypeByDefaultValue ( enable bool ) { v . SetTypeByDefaultValue ( enable ) }
2020-09-11 18:48:38 +03:00
[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 18:54:20 +03:00
func ( v * Viper ) SetTypeByDefaultValue ( enable bool ) {
v . typeByDefValue = enable
}
2016-08-06 19:06:49 +03:00
// GetViper gets the global Viper instance.
func GetViper ( ) * Viper {
return v
}
2016-09-20 11:17:41 +03:00
// Get can retrieve any value given the key to use.
2016-10-30 00:33:52 +03:00
// Get is case-insensitive for a key.
2014-12-05 19:04:40 +03:00
// 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:
2015-04-02 00:08:42 +03:00
// override, flag, env, config file, key/value store, default
2014-12-05 19:04:40 +03:00
//
// Get returns an interface. For a specific value use one of the Get____ methods.
2023-09-26 17:59:38 +03:00
func Get ( key string ) any { return v . Get ( key ) }
2020-09-11 18:48:38 +03:00
2023-09-26 17:59:38 +03:00
func ( v * Viper ) Get ( key string ) any {
[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 18:54:20 +03:00
lcaseKey := strings . ToLower ( key )
2017-04-11 23:15:44 +03:00
val := v . find ( lcaseKey , true )
2015-11-10 01:58:46 +03:00
if val == nil {
return nil
}
2016-10-08 11:00:18 +03:00
if v . typeByDefValue {
2016-10-10 14:40:38 +03:00
// TODO(bep) this branch isn't covered by a single test.
2017-06-19 13:35:39 +03:00
valType := val
2016-10-14 12:24:45 +03:00
path := strings . Split ( lcaseKey , v . keyDelim )
defVal := v . searchMap ( v . defaults , path )
2016-10-08 11:00:18 +03:00
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 18:54:20 +03:00
valType = defVal
}
2017-06-19 13:35:39 +03:00
switch valType . ( type ) {
case bool :
return cast . ToBool ( val )
case string :
return cast . ToString ( val )
2018-09-01 23:59:01 +03:00
case int32 , int16 , int8 , int :
2017-06-19 13:35:39 +03:00
return cast . ToInt ( val )
2019-04-08 17:06:45 +03:00
case uint :
return cast . ToUint ( val )
case uint32 :
return cast . ToUint32 ( val )
case uint64 :
return cast . ToUint64 ( val )
2018-09-01 23:59:01 +03:00
case int64 :
return cast . ToInt64 ( val )
2017-06-19 13:35:39 +03:00
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 )
2019-06-11 23:51:57 +03:00
case [ ] int :
return cast . ToIntSlice ( val )
2023-02-05 20:30:05 +03:00
case [ ] time . Duration :
return cast . ToDurationSlice ( val )
2017-06-19 13:35:39 +03:00
}
2014-12-05 19:04:40 +03:00
}
2017-06-19 13:35:39 +03:00
2014-12-05 19:04:40 +03:00
return val
}
2016-09-20 11:17:41 +03:00
// Sub returns new Viper instance representing a sub tree of this instance.
2016-10-30 00:33:52 +03:00
// Sub is case-insensitive for a key.
2015-12-24 14:44:44 +03:00
func Sub ( key string ) * Viper { return v . Sub ( key ) }
2020-09-11 18:48:38 +03:00
2015-12-24 14:44:44 +03:00
func ( v * Viper ) Sub ( key string ) * Viper {
2015-12-25 07:29:33 +03:00
subv := New ( )
data := v . Get ( key )
2016-10-06 02:22:39 +03:00
if data == nil {
return nil
}
2015-12-25 07:29:33 +03:00
if reflect . TypeOf ( data ) . Kind ( ) == reflect . Map {
2023-11-30 18:45:20 +03:00
subv . parents = append ( [ ] string ( nil ) , v . parents ... )
subv . parents = append ( subv . parents , strings . ToLower ( key ) )
2020-12-24 09:04:37 +03:00
subv . automaticEnvApplied = v . automaticEnvApplied
2021-04-04 23:16:36 +03:00
subv . envPrefix = v . envPrefix
2020-12-24 09:04:37 +03:00
subv . envKeyReplacer = v . envKeyReplacer
2015-12-25 07:29:33 +03:00
subv . config = cast . ToStringMap ( data )
return subv
2015-12-24 14:44:44 +03:00
}
2016-09-20 11:17:41 +03:00
return nil
2015-12-24 14:44:44 +03:00
}
2016-09-20 11:17:41 +03:00
// GetString returns the value associated with the key as a string.
2014-12-05 05:55:51 +03:00
func GetString ( key string ) string { return v . GetString ( key ) }
2020-09-11 18:48:38 +03:00
2015-02-17 17:22:37 +03:00
func ( v * Viper ) GetString ( key string ) string {
2014-12-09 15:42:09 +03:00
return cast . ToString ( v . Get ( key ) )
2014-04-05 01:21:59 +04:00
}
2016-09-20 11:17:41 +03:00
// GetBool returns the value associated with the key as a boolean.
2014-12-05 05:55:51 +03:00
func GetBool ( key string ) bool { return v . GetBool ( key ) }
2020-09-11 18:48:38 +03:00
2015-02-17 17:22:37 +03:00
func ( v * Viper ) GetBool ( key string ) bool {
2014-12-09 15:42:09 +03:00
return cast . ToBool ( v . Get ( key ) )
2014-04-05 01:21:59 +04:00
}
2016-09-20 11:17:41 +03:00
// GetInt returns the value associated with the key as an integer.
2014-12-05 05:55:51 +03:00
func GetInt ( key string ) int { return v . GetInt ( key ) }
2020-09-11 18:48:38 +03:00
2015-02-17 17:22:37 +03:00
func ( v * Viper ) GetInt ( key string ) int {
2014-12-09 15:42:09 +03:00
return cast . ToInt ( v . Get ( key ) )
2014-04-05 01:21:59 +04:00
}
2018-05-06 04:38:06 +03:00
// GetInt32 returns the value associated with the key as an integer.
func GetInt32 ( key string ) int32 { return v . GetInt32 ( key ) }
2020-09-11 18:48:38 +03:00
2018-05-06 04:38:06 +03:00
func ( v * Viper ) GetInt32 ( key string ) int32 {
return cast . ToInt32 ( v . Get ( key ) )
}
2016-09-20 11:17:41 +03:00
// GetInt64 returns the value associated with the key as an integer.
2016-08-05 10:16:55 +03:00
func GetInt64 ( key string ) int64 { return v . GetInt64 ( key ) }
2020-09-11 18:48:38 +03:00
2016-08-05 10:16:55 +03:00
func ( v * Viper ) GetInt64 ( key string ) int64 {
return cast . ToInt64 ( v . Get ( key ) )
}
2019-04-08 17:06:45 +03:00
// GetUint returns the value associated with the key as an unsigned integer.
func GetUint ( key string ) uint { return v . GetUint ( key ) }
2020-09-11 18:48:38 +03:00
2019-04-08 17:06:45 +03:00
func ( v * Viper ) GetUint ( key string ) uint {
return cast . ToUint ( v . Get ( key ) )
}
2022-07-19 10:58:01 +03:00
// GetUint16 returns the value associated with the key as an unsigned integer.
func GetUint16 ( key string ) uint16 { return v . GetUint16 ( key ) }
func ( v * Viper ) GetUint16 ( key string ) uint16 {
return cast . ToUint16 ( v . Get ( key ) )
}
2019-04-08 17:06:45 +03:00
// GetUint32 returns the value associated with the key as an unsigned integer.
func GetUint32 ( key string ) uint32 { return v . GetUint32 ( key ) }
2020-09-11 18:48:38 +03:00
2019-04-08 17:06:45 +03:00
func ( v * Viper ) GetUint32 ( key string ) uint32 {
return cast . ToUint32 ( v . Get ( key ) )
}
// GetUint64 returns the value associated with the key as an unsigned integer.
func GetUint64 ( key string ) uint64 { return v . GetUint64 ( key ) }
2020-09-11 18:48:38 +03:00
2019-04-08 17:06:45 +03:00
func ( v * Viper ) GetUint64 ( key string ) uint64 {
return cast . ToUint64 ( v . Get ( key ) )
}
2016-09-20 11:17:41 +03:00
// GetFloat64 returns the value associated with the key as a float64.
2014-12-05 05:55:51 +03:00
func GetFloat64 ( key string ) float64 { return v . GetFloat64 ( key ) }
2020-09-11 18:48:38 +03:00
2015-02-17 17:22:37 +03:00
func ( v * Viper ) GetFloat64 ( key string ) float64 {
2014-12-09 15:42:09 +03:00
return cast . ToFloat64 ( v . Get ( key ) )
2014-04-05 01:21:59 +04:00
}
2016-09-20 11:17:41 +03:00
// GetTime returns the value associated with the key as time.
2014-12-05 05:55:51 +03:00
func GetTime ( key string ) time . Time { return v . GetTime ( key ) }
2020-09-11 18:48:38 +03:00
2015-02-17 17:22:37 +03:00
func ( v * Viper ) GetTime ( key string ) time . Time {
2014-12-09 15:42:09 +03:00
return cast . ToTime ( v . Get ( key ) )
2014-04-05 01:21:59 +04:00
}
2016-09-20 11:17:41 +03:00
// GetDuration returns the value associated with the key as a duration.
2015-02-19 06:03:20 +03:00
func GetDuration ( key string ) time . Duration { return v . GetDuration ( key ) }
2020-09-11 18:48:38 +03:00
2015-02-17 17:22:37 +03:00
func ( v * Viper ) GetDuration ( key string ) time . Duration {
2015-02-19 06:03:20 +03:00
return cast . ToDuration ( v . Get ( key ) )
}
2019-07-13 12:57:28 +03:00
// GetIntSlice returns the value associated with the key as a slice of int values.
2019-06-19 17:05:58 +03:00
func GetIntSlice ( key string ) [ ] int { return v . GetIntSlice ( key ) }
2020-09-11 18:48:38 +03:00
2019-06-19 17:05:58 +03:00
func ( v * Viper ) GetIntSlice ( key string ) [ ] int {
return cast . ToIntSlice ( v . Get ( key ) )
}
2016-09-20 11:17:41 +03:00
// GetStringSlice returns the value associated with the key as a slice of strings.
2014-12-05 05:55:51 +03:00
func GetStringSlice ( key string ) [ ] string { return v . GetStringSlice ( key ) }
2020-09-11 18:48:38 +03:00
2015-02-17 17:22:37 +03:00
func ( v * Viper ) GetStringSlice ( key string ) [ ] string {
2014-12-09 15:42:09 +03:00
return cast . ToStringSlice ( v . Get ( key ) )
2014-04-05 09:19:39 +04:00
}
2016-09-20 11:17:41 +03:00
// GetStringMap returns the value associated with the key as a map of interfaces.
2023-09-26 17:59:38 +03:00
func GetStringMap ( key string ) map [ string ] any { return v . GetStringMap ( key ) }
2020-09-11 18:48:38 +03:00
2023-09-26 17:59:38 +03:00
func ( v * Viper ) GetStringMap ( key string ) map [ string ] any {
2014-12-09 15:42:09 +03:00
return cast . ToStringMap ( v . Get ( key ) )
2014-04-05 09:19:39 +04:00
}
2016-09-20 11:17:41 +03:00
// GetStringMapString returns the value associated with the key as a map of strings.
2014-12-05 05:55:51 +03:00
func GetStringMapString ( key string ) map [ string ] string { return v . GetStringMapString ( key ) }
2020-09-11 18:48:38 +03:00
2015-02-17 17:22:37 +03:00
func ( v * Viper ) GetStringMapString ( key string ) map [ string ] string {
2014-12-09 15:42:09 +03:00
return cast . ToStringMapString ( v . Get ( key ) )
2014-04-05 01:21:59 +04:00
}
2016-09-20 11:17:41 +03:00
// GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings.
2015-07-30 23:43:18 +03:00
func GetStringMapStringSlice ( key string ) map [ string ] [ ] string { return v . GetStringMapStringSlice ( key ) }
2020-09-11 18:48:38 +03:00
2015-07-30 23:46:38 +03:00
func ( v * Viper ) GetStringMapStringSlice ( key string ) map [ string ] [ ] string {
2015-07-30 23:27:34 +03:00
return cast . ToStringMapStringSlice ( v . Get ( key ) )
}
2016-09-20 11:17:41 +03:00
// GetSizeInBytes returns the size of the value associated with the given key
2015-04-02 00:08:42 +03:00
// in bytes.
2015-03-01 00:03:22 +03:00
func GetSizeInBytes ( key string ) uint { return v . GetSizeInBytes ( key ) }
2020-09-11 18:48:38 +03:00
2015-02-17 17:22:37 +03:00
func ( v * Viper ) GetSizeInBytes ( key string ) uint {
2015-03-01 00:03:22 +03:00
sizeStr := cast . ToString ( v . Get ( key ) )
return parseSizeInBytes ( sizeStr )
}
2016-09-20 11:17:41 +03:00
// UnmarshalKey takes a single key and unmarshals it into a Struct.
2023-09-26 17:59:38 +03:00
func UnmarshalKey ( key string , rawVal any , opts ... DecoderConfigOption ) error {
2018-06-28 12:55:33 +03:00
return v . UnmarshalKey ( key , rawVal , opts ... )
}
2020-09-11 18:48:38 +03:00
2023-09-26 17:59:38 +03:00
func ( v * Viper ) UnmarshalKey ( key string , rawVal any , opts ... DecoderConfigOption ) error {
2020-05-21 09:55:41 +03:00
return decode ( v . Get ( key ) , defaultDecoderConfig ( rawVal , opts ... ) )
2014-06-27 01:58:55 +04:00
}
2016-09-20 11:17:41 +03:00
// Unmarshal unmarshals the config into a Struct. Make sure that the tags
2015-04-02 00:08:42 +03:00
// on the fields of the structure are properly set.
2023-09-26 17:59:38 +03:00
func Unmarshal ( rawVal any , opts ... DecoderConfigOption ) error {
2018-06-28 12:55:33 +03:00
return v . Unmarshal ( rawVal , opts ... )
}
2020-09-11 18:48:38 +03:00
2023-09-26 17:59:38 +03:00
func ( v * Viper ) Unmarshal ( rawVal any , opts ... DecoderConfigOption ) error {
2023-12-15 15:31:38 +03:00
keys := v . AllKeys ( )
if features . BindStruct {
// TODO: make this optional?
structKeys , err := v . decodeStructKeys ( rawVal , opts ... )
if err != nil {
return err
}
keys = append ( keys , structKeys ... )
2023-12-05 17:40:27 +03:00
}
// TODO: struct keys should be enough?
2024-01-03 07:17:45 +03:00
err := decode ( v . getSettings ( keys ) , defaultDecoderConfig ( rawVal , opts ... ) )
// Post processing for slice of maps
// if features.BindStruct {
err = unmarshalPostProcess ( rawVal , opts ... )
if err != nil {
return err
}
// }
return err
}
func unmarshalPostProcess ( input any , opts ... DecoderConfigOption ) error {
var structKeyMap map [ string ] any
err := decode ( input , defaultDecoderConfig ( & structKeyMap , opts ... ) )
if err != nil {
return err
}
v . postProcessingSliceFields ( map [ string ] bool { } , structKeyMap , "" )
return nil
}
// TODO remove shadow
func ( v * Viper ) postProcessingSliceFields ( shadow map [ string ] bool , m map [ string ] any , prefix string ) map [ string ] bool {
if shadow != nil && prefix != "" && shadow [ prefix ] {
// prefix is shadowed => nothing more to flatten
return shadow
}
if shadow == nil {
shadow = make ( map [ string ] bool )
}
var m2 map [ string ] any
if prefix != "" {
prefix += v . keyDelim
}
for k , val := range m {
fullKey := prefix + k
valValue := reflect . ValueOf ( val )
if valValue . Kind ( ) == reflect . Slice {
for i := 0 ; i < valValue . Len ( ) ; i ++ {
item := valValue . Index ( i )
if item . Kind ( ) != reflect . Struct || ! item . CanSet ( ) {
continue
}
itemType := item . Type ( )
for j := 0 ; j < item . NumField ( ) ; j ++ {
field := itemType . Field ( j )
// fmt.Printf("Field %d: Name=%s, Type=%v, Value=%v\n", j, field.Name, field.Type, item.Field(j).Interface())
sliceKey := fmt . Sprintf ( "%s%s%s%d%s%s" , prefix , k , v . keyDelim , i , v . keyDelim , field . Name )
shadow [ strings . ToLower ( sliceKey ) ] = true
// fmt.Printf("%s is slice\n", sliceKey)
if val , ok := v . getEnv ( v . mergeWithEnvPrefix ( sliceKey ) ) ; ok {
// fmt.Printf("Val is %v\n", val)
item . Field ( j ) . SetString ( val )
}
}
}
}
switch val := val . ( type ) {
case map [ string ] any :
m2 = val
case map [ any ] any :
m2 = cast . ToStringMap ( val )
default :
continue
}
// recursively merge to shadow map
shadow = v . postProcessingSliceFields ( shadow , m2 , fullKey )
}
return shadow
2023-12-05 17:40:27 +03:00
}
func ( v * Viper ) decodeStructKeys ( input any , opts ... DecoderConfigOption ) ( [ ] string , error ) {
var structKeyMap map [ string ] any
err := decode ( input , defaultDecoderConfig ( & structKeyMap , opts ... ) )
if err != nil {
return nil , err
}
flattenedStructKeyMap := v . flattenAndMergeMap ( map [ string ] bool { } , structKeyMap , "" )
r := make ( [ ] string , 0 , len ( flattenedStructKeyMap ) )
for v := range flattenedStructKeyMap {
r = append ( r , v )
}
return r , nil
2015-12-30 09:11:39 +03:00
}
2023-03-31 13:38:27 +03:00
// defaultDecoderConfig returns default mapstructure.DecoderConfig with support
2023-10-09 17:52:53 +03:00
// of time.Duration values & string slices.
2023-09-26 17:59:38 +03:00
func defaultDecoderConfig ( output any , opts ... DecoderConfigOption ) * mapstructure . DecoderConfig {
2018-06-28 12:55:33 +03:00
c := & mapstructure . DecoderConfig {
2015-12-30 09:11:39 +03:00
Metadata : nil ,
Result : output ,
WeaklyTypedInput : true ,
2017-09-30 00:06:42 +03:00
DecodeHook : mapstructure . ComposeDecodeHookFunc (
2017-03-03 14:09:20 +03:00
mapstructure . StringToTimeDurationHookFunc ( ) ,
mapstructure . StringToSliceHookFunc ( "," ) ,
) ,
2015-12-30 09:11:39 +03:00
}
2018-06-28 12:55:33 +03:00
for _ , opt := range opts {
opt ( c )
}
return c
2016-09-24 02:20:44 +03:00
}
2015-12-30 09:11:39 +03:00
2023-10-09 17:52:53 +03:00
// decode is a wrapper around mapstructure.Decode that mimics the WeakDecode functionality.
2023-09-26 17:59:38 +03:00
func decode ( input any , config * mapstructure . DecoderConfig ) error {
2015-12-30 09:11:39 +03:00
decoder , err := mapstructure . NewDecoder ( config )
if err != nil {
return err
}
return decoder . Decode ( input )
}
2016-09-20 11:17:41 +03:00
// UnmarshalExact unmarshals the config into a Struct, erroring if a field is nonexistent
// in the destination struct.
2023-09-26 17:59:38 +03:00
func UnmarshalExact ( rawVal any , opts ... DecoderConfigOption ) error {
2019-12-06 16:23:11 +03:00
return v . UnmarshalExact ( rawVal , opts ... )
}
2020-09-11 18:48:38 +03:00
2023-09-26 17:59:38 +03:00
func ( v * Viper ) UnmarshalExact ( rawVal any , opts ... DecoderConfigOption ) error {
2019-12-06 16:23:11 +03:00
config := defaultDecoderConfig ( rawVal , opts ... )
2016-09-24 02:20:44 +03:00
config . ErrorUnused = true
2023-12-15 15:31:38 +03:00
keys := v . AllKeys ( )
if features . BindStruct {
// TODO: make this optional?
structKeys , err := v . decodeStructKeys ( rawVal , opts ... )
if err != nil {
return err
}
keys = append ( keys , structKeys ... )
2023-12-07 00:20:47 +03:00
}
// TODO: struct keys should be enough?
2023-12-15 15:31:38 +03:00
return decode ( v . getSettings ( keys ) , config )
2014-06-27 01:58:55 +04:00
}
2016-09-20 11:17:41 +03:00
// BindPFlags binds a full flag set to the configuration, using each flag's long
2015-04-02 04:38:54 +03:00
// name as the config key.
2016-09-20 11:17:41 +03:00
func BindPFlags ( flags * pflag . FlagSet ) error { return v . BindPFlags ( flags ) }
2020-09-11 18:48:38 +03:00
2016-09-20 11:17:41 +03:00
func ( v * Viper ) BindPFlags ( flags * pflag . FlagSet ) error {
2015-12-10 21:14:17 +03:00
return v . BindFlagValues ( pflagValueSet { flags } )
}
2016-09-20 11:17:41 +03:00
// BindPFlag binds a specific key to a pflag (as used by cobra).
2016-08-05 10:25:24 +03:00
// Example (where serverCmd is a Cobra instance):
2015-12-10 21:14:17 +03:00
//
2022-09-03 19:05:45 +03:00
// serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
// Viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
2016-09-20 11:17:41 +03:00
func BindPFlag ( key string , flag * pflag . Flag ) error { return v . BindPFlag ( key , flag ) }
2020-09-11 18:48:38 +03:00
2016-09-20 11:17:41 +03:00
func ( v * Viper ) BindPFlag ( key string , flag * pflag . Flag ) error {
2020-08-12 02:26:27 +03:00
if flag == nil {
return fmt . Errorf ( "flag for %q is nil" , key )
}
2015-12-10 21:14:17 +03:00
return v . BindFlagValue ( key , pflagValue { flag } )
}
2016-09-20 11:17:41 +03:00
// BindFlagValues binds a full FlagValue set to the configuration, using each flag's long
2015-12-10 21:14:17 +03:00
// name as the config key.
2016-09-20 11:17:41 +03:00
func BindFlagValues ( flags FlagValueSet ) error { return v . BindFlagValues ( flags ) }
2020-09-11 18:48:38 +03:00
2015-12-10 21:14:17 +03:00
func ( v * Viper ) BindFlagValues ( flags FlagValueSet ) ( err error ) {
flags . VisitAll ( func ( flag FlagValue ) {
if err = v . BindFlagValue ( flag . Name ( ) , flag ) ; err != nil {
2015-04-02 04:38:54 +03:00
return
}
} )
2015-11-10 01:58:46 +03:00
return nil
2015-04-02 04:38:54 +03:00
}
2016-09-20 11:17:41 +03:00
// BindFlagValue binds a specific key to a FlagValue.
func BindFlagValue ( key string , flag FlagValue ) error { return v . BindFlagValue ( key , flag ) }
2020-09-11 18:48:38 +03:00
2016-09-20 11:17:41 +03:00
func ( v * Viper ) BindFlagValue ( key string , flag FlagValue ) error {
2014-06-27 20:29:37 +04:00
if flag == nil {
return fmt . Errorf ( "flag for %q is nil" , key )
}
2014-12-05 05:55:51 +03:00
v . pflags [ strings . ToLower ( key ) ] = flag
2014-06-27 20:29:37 +04:00
return nil
}
2016-09-20 11:17:41 +03:00
// BindEnv binds a Viper key to a ENV variable.
// ENV variables are case sensitive.
2014-09-28 01:03:00 +04:00
// If only a key is provided, it will use the env key matching the key, uppercased.
2020-09-30 13:24:59 +03:00
// If more arguments are provided, they will represent the env variable names that
// should bind to this key and will be taken in the specified order.
2014-12-23 02:31:11 +03:00
// EnvPrefix will be used when set when env name is not provided.
2016-09-20 11:17:41 +03:00
func BindEnv ( input ... string ) error { return v . BindEnv ( input ... ) }
2020-09-11 18:48:38 +03:00
2016-09-20 11:17:41 +03:00
func ( v * Viper ) BindEnv ( input ... string ) error {
2014-09-28 01:03:00 +04:00
if len ( input ) == 0 {
2019-12-06 14:46:23 +03:00
return fmt . Errorf ( "missing key to bind to" )
2014-09-28 01:03:00 +04:00
}
2020-09-10 13:08:26 +03:00
key := strings . ToLower ( input [ 0 ] )
2014-09-28 01:03:00 +04:00
if len ( input ) == 1 {
2020-09-10 13:08:26 +03:00
v . env [ key ] = append ( v . env [ key ] , v . mergeWithEnvPrefix ( key ) )
2014-09-28 01:03:00 +04:00
} else {
2020-09-10 13:08:26 +03:00
v . env [ key ] = append ( v . env [ key ] , input [ 1 : ] ... )
2014-09-28 01:03:00 +04:00
}
return nil
}
2022-02-28 18:03:17 +03:00
// MustBindEnv wraps BindEnv in a panic.
// If there is an error binding an environment variable, MustBindEnv will
// panic.
func MustBindEnv ( input ... string ) { v . MustBindEnv ( input ... ) }
func ( v * Viper ) MustBindEnv ( input ... string ) {
if err := v . BindEnv ( input ... ) ; err != nil {
panic ( fmt . Sprintf ( "error while binding environment variable: %v" , err ) )
}
}
2016-09-20 11:17:41 +03:00
// Given a key, find the value.
2017-04-11 23:15:44 +03:00
//
2016-09-20 11:17:41 +03:00
// Viper will check to see if an alias exists first.
2017-04-11 23:15:44 +03: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.
//
2016-10-10 14:40:38 +03:00
// Note: this assumes a lower-cased key given.
2023-09-26 17:59:38 +03:00
func ( v * Viper ) find ( lcaseKey string , flagDefault bool ) any {
2016-10-10 14:40:38 +03:00
var (
2023-09-26 17:59:38 +03:00
val any
2016-10-10 14:40:38 +03:00
exists bool
path = strings . Split ( lcaseKey , v . keyDelim )
nested = len ( path ) > 1
)
2014-04-05 01:21:59 +04:00
2016-10-08 11:00:18 +03:00
// compute the path through the nested maps to the nested value
2016-10-10 14:40:38 +03:00
if nested && v . isPathShadowedInDeepMap ( path , castMapStringToMapInterface ( v . aliases ) ) != "" {
2016-10-08 11:00:18 +03:00
return nil
}
2014-04-05 09:19:39 +04:00
// if the requested key is an alias, then return the proper key
2016-10-10 14:40:38 +03:00
lcaseKey = v . realKey ( lcaseKey )
2016-10-14 12:24:45 +03:00
path = strings . Split ( lcaseKey , v . keyDelim )
nested = len ( path ) > 1
2014-04-05 01:21:59 +04:00
2016-10-08 11:00:18 +03:00
// Set() override first
2016-10-14 12:24:45 +03:00
val = v . searchMap ( v . override , path )
2016-10-08 11:00:18 +03:00
if val != nil {
return val
}
2016-10-10 14:40:38 +03:00
if nested && v . isPathShadowedInDeepMap ( path , v . override ) != "" {
2016-10-08 11:00:18 +03:00
return nil
}
// PFlag override next
2016-10-10 14:40:38 +03:00
flag , exists := v . pflags [ lcaseKey ]
2015-12-10 21:14:17 +03:00
if exists && flag . HasChanged ( ) {
switch flag . ValueType ( ) {
2015-11-10 01:58:46 +03:00
case "int" , "int8" , "int16" , "int32" , "int64" :
2015-12-10 21:14:17 +03:00
return cast . ToInt ( flag . ValueString ( ) )
2015-11-10 01:58:46 +03:00
case "bool" :
2015-12-10 21:14:17 +03:00
return cast . ToBool ( flag . ValueString ( ) )
2021-06-23 14:24:02 +03:00
case "stringSlice" , "stringArray" :
2016-09-22 21:19:24 +03:00
s := strings . TrimPrefix ( flag . ValueString ( ) , "[" )
2017-04-17 11:08:15 +03:00
s = strings . TrimSuffix ( s , "]" )
res , _ := readAsCSV ( s )
return res
2019-06-11 23:51:57 +03:00
case "intSlice" :
s := strings . TrimPrefix ( flag . ValueString ( ) , "[" )
s = strings . TrimSuffix ( s , "]" )
res , _ := readAsCSV ( s )
return cast . ToIntSlice ( res )
2023-02-05 20:30:05 +03:00
case "durationSlice" :
s := strings . TrimPrefix ( flag . ValueString ( ) , "[" )
s = strings . TrimSuffix ( s , "]" )
slice := strings . Split ( s , "," )
return cast . ToDurationSlice ( slice )
2020-05-09 12:38:39 +03:00
case "stringToString" :
return stringToStringConv ( flag . ValueString ( ) )
2023-02-07 14:10:59 +03:00
case "stringToInt" :
return stringToIntConv ( flag . ValueString ( ) )
2015-11-10 01:58:46 +03:00
default :
2015-12-10 21:14:17 +03:00
return flag . ValueString ( )
2014-06-27 20:29:37 +04:00
}
}
2016-10-10 14:40:38 +03:00
if nested && v . isPathShadowedInFlatMap ( path , v . pflags ) != "" {
2016-10-08 11:00:18 +03:00
return nil
2014-04-05 01:21:59 +04:00
}
2016-10-08 11:00:18 +03:00
// Env override next
2014-12-23 06:47:25 +03:00
if v . automaticEnvApplied {
2020-12-24 09:04:37 +03:00
envKey := strings . Join ( append ( v . parents , lcaseKey ) , "." )
2014-12-23 06:47:25 +03:00
// even if it hasn't been registered, if automaticEnv is used,
// check any Get request
2020-12-24 09:04:37 +03:00
if val , ok := v . getEnv ( v . mergeWithEnvPrefix ( envKey ) ) ; ok {
2014-12-23 06:47:25 +03:00
return val
}
2016-10-10 14:40:38 +03:00
if nested && v . isPathShadowedInAutoEnv ( path ) != "" {
2016-10-08 11:00:18 +03:00
return nil
}
2014-12-23 06:47:25 +03:00
}
2020-09-10 13:08:26 +03:00
envkeys , exists := v . env [ lcaseKey ]
2014-09-28 01:03:00 +04:00
if exists {
2020-09-10 13:08:26 +03:00
for _ , envkey := range envkeys {
if val , ok := v . getEnv ( envkey ) ; ok {
return val
}
2014-09-28 01:03:00 +04:00
}
}
2016-10-14 12:24:45 +03:00
if nested && v . isPathShadowedInFlatMap ( path , v . env ) != "" {
2016-10-08 11:00:18 +03:00
return nil
}
2014-09-28 01:03:00 +04:00
2016-10-08 11:00:18 +03:00
// Config file next
2020-10-04 21:07:34 +03:00
val = v . searchIndexableWithPathPrefixes ( v . config , path )
2016-10-08 11:00:18 +03:00
if val != nil {
2014-04-05 01:21:59 +04:00
return val
}
2016-10-14 12:24:45 +03:00
if nested && v . isPathShadowedInDeepMap ( path , v . config ) != "" {
2016-10-08 11:00:18 +03:00
return nil
2015-10-14 01:31:32 +03:00
}
2016-10-08 11:00:18 +03:00
// K/V store next
val = v . searchMap ( v . kvstore , path )
if val != nil {
2014-10-24 23:38:01 +04:00
return val
}
2016-10-14 12:24:45 +03:00
if nested && v . isPathShadowedInDeepMap ( path , v . kvstore ) != "" {
2016-10-08 11:00:18 +03:00
return nil
}
2014-10-24 23:38:01 +04:00
2016-10-08 11:00:18 +03:00
// Default next
val = v . searchMap ( v . defaults , path )
if val != nil {
2014-04-05 01:21:59 +04:00
return val
}
2016-10-14 12:24:45 +03:00
if nested && v . isPathShadowedInDeepMap ( path , v . defaults ) != "" {
2016-10-08 11:00:18 +03:00
return nil
}
2017-04-11 23:15:44 +03: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 ( ) )
2021-06-23 14:24:02 +03:00
case "stringSlice" , "stringArray" :
2017-04-11 23:15:44 +03:00
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 )
2020-05-09 12:38:39 +03:00
case "stringToString" :
return stringToStringConv ( flag . ValueString ( ) )
2023-02-07 14:10:59 +03:00
case "stringToInt" :
return stringToIntConv ( flag . ValueString ( ) )
2023-02-05 20:30:05 +03:00
case "durationSlice" :
s := strings . TrimPrefix ( flag . ValueString ( ) , "[" )
s = strings . TrimSuffix ( s , "]" )
slice := strings . Split ( s , "," )
return cast . ToDurationSlice ( slice )
2017-04-11 23:15:44 +03:00
default :
return flag . ValueString ( )
}
2016-10-08 11:00:18 +03:00
}
2017-04-11 23:15:44 +03:00
// last item, no need to check shadowing
2016-10-08 11:00:18 +03:00
}
2014-04-05 01:21:59 +04:00
return nil
}
2017-04-17 11:08:15 +03:00
func readAsCSV ( val string ) ( [ ] string , error ) {
if val == "" {
return [ ] string { } , nil
}
stringReader := strings . NewReader ( val )
csvReader := csv . NewReader ( stringReader )
return csvReader . Read ( )
}
2020-05-09 12:38:39 +03:00
// mostly copied from pflag's implementation of this operation here https://github.com/spf13/pflag/blob/master/string_to_string.go#L79
2023-10-09 17:52:53 +03:00
// alterations are: errors are swallowed, map[string]any is returned in order to enable cast.ToStringMap.
2023-09-26 17:59:38 +03:00
func stringToStringConv ( val string ) any {
2020-05-09 12:38:39 +03:00
val = strings . Trim ( val , "[]" )
// An empty string would cause an empty map
2023-11-30 18:45:20 +03:00
if val == "" {
2023-09-26 17:59:38 +03:00
return map [ string ] any { }
2020-05-09 12:38:39 +03:00
}
r := csv . NewReader ( strings . NewReader ( val ) )
ss , err := r . Read ( )
if err != nil {
return nil
}
2023-09-26 17:59:38 +03:00
out := make ( map [ string ] any , len ( ss ) )
2020-05-09 12:38:39 +03:00
for _ , pair := range ss {
2023-10-04 20:52:28 +03:00
k , vv , found := strings . Cut ( pair , "=" )
if ! found {
2020-05-09 12:38:39 +03:00
return nil
}
2023-10-04 20:52:28 +03:00
out [ k ] = vv
2020-05-09 12:38:39 +03:00
}
return out
}
2023-02-07 14:10:59 +03:00
// mostly copied from pflag's implementation of this operation here https://github.com/spf13/pflag/blob/d5e0c0615acee7028e1e2740a11102313be88de1/string_to_int.go#L68
2023-10-09 17:52:53 +03:00
// alterations are: errors are swallowed, map[string]any is returned in order to enable cast.ToStringMap.
2023-09-26 17:59:38 +03:00
func stringToIntConv ( val string ) any {
2023-02-07 14:10:59 +03:00
val = strings . Trim ( val , "[]" )
// An empty string would cause an empty map
2023-11-30 18:45:20 +03:00
if val == "" {
2023-09-26 17:59:38 +03:00
return map [ string ] any { }
2023-02-07 14:10:59 +03:00
}
ss := strings . Split ( val , "," )
2023-09-26 17:59:38 +03:00
out := make ( map [ string ] any , len ( ss ) )
2023-02-07 14:10:59 +03:00
for _ , pair := range ss {
2023-10-04 20:52:28 +03:00
k , vv , found := strings . Cut ( pair , "=" )
if ! found {
2023-02-07 14:10:59 +03:00
return nil
}
var err error
2023-10-04 20:52:28 +03:00
out [ k ] , err = strconv . Atoi ( vv )
2023-02-07 14:10:59 +03:00
if err != nil {
return nil
}
}
return out
}
2016-09-20 11:17:41 +03:00
// IsSet checks to see if the key has been set in any of the data locations.
2016-10-30 00:33:52 +03:00
// IsSet is case-insensitive for a key.
2014-12-05 05:55:51 +03:00
func IsSet ( key string ) bool { return v . IsSet ( key ) }
2020-09-11 18:48:38 +03:00
2015-02-17 17:22:37 +03:00
func ( v * Viper ) IsSet ( key string ) bool {
2015-11-30 02:16:21 +03:00
lcaseKey := strings . ToLower ( key )
2017-04-11 23:15:44 +03:00
val := v . find ( lcaseKey , false )
2015-11-30 02:16:21 +03:00
return val != nil
2014-04-05 01:21:59 +04:00
}
2020-10-08 03:46:11 +03:00
// AutomaticEnv makes Viper check if environment variables match any of the existing keys
// (config, default or flags). If matching env vars are found, they are loaded into Viper.
2014-12-05 05:55:51 +03:00
func AutomaticEnv ( ) { v . AutomaticEnv ( ) }
2020-09-11 18:48:38 +03:00
2015-02-17 17:22:37 +03:00
func ( v * Viper ) AutomaticEnv ( ) {
2014-12-23 06:47:25 +03:00
v . automaticEnvApplied = true
2014-09-28 01:01:11 +04:00
}
2015-03-06 22:21:17 +03:00
// SetEnvKeyReplacer sets the strings.Replacer on the viper object
2015-04-02 00:08:42 +03:00
// Useful for mapping an environmental variable to a key that does
// not match it.
2015-03-06 22:21:17 +03:00
func SetEnvKeyReplacer ( r * strings . Replacer ) { v . SetEnvKeyReplacer ( r ) }
2020-09-11 18:48:38 +03:00
2015-02-17 17:22:37 +03:00
func ( v * Viper ) SetEnvKeyReplacer ( r * strings . Replacer ) {
2015-03-06 22:21:17 +03:00
v . envKeyReplacer = r
}
2019-07-13 13:04:26 +03:00
// RegisterAlias creates an alias that provides another accessor for the same key.
// This enables one to change a name without breaking the application.
2023-11-30 18:45:20 +03:00
func RegisterAlias ( alias , key string ) { v . RegisterAlias ( alias , key ) }
2020-09-11 18:48:38 +03:00
2023-11-30 18:45:20 +03:00
func ( v * Viper ) RegisterAlias ( alias , key string ) {
2014-12-05 05:55:51 +03:00
v . registerAlias ( alias , strings . ToLower ( key ) )
2014-04-05 09:19:39 +04:00
}
2023-11-30 18:45:20 +03:00
func ( v * Viper ) registerAlias ( alias , key string ) {
2014-04-05 09:19:39 +04:00
alias = strings . ToLower ( alias )
2014-12-05 05:55:51 +03:00
if alias != key && alias != v . realKey ( key ) {
_ , exists := v . aliases [ alias ]
2014-08-05 15:35:21 +04:00
2014-04-05 09:19:39 +04:00
if ! exists {
2014-08-05 15:35:21 +04:00
// 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.
2014-12-05 05:55:51 +03:00
if val , ok := v . config [ alias ] ; ok {
delete ( v . config , alias )
v . config [ key ] = val
2014-08-05 15:35:21 +04:00
}
2014-12-05 05:55:51 +03:00
if val , ok := v . kvstore [ alias ] ; ok {
delete ( v . kvstore , alias )
v . kvstore [ key ] = val
2014-10-24 23:38:01 +04:00
}
2014-12-05 05:55:51 +03:00
if val , ok := v . defaults [ alias ] ; ok {
delete ( v . defaults , alias )
v . defaults [ key ] = val
2014-08-05 15:35:21 +04:00
}
2014-12-05 05:55:51 +03:00
if val , ok := v . override [ alias ] ; ok {
delete ( v . override , alias )
v . override [ key ] = val
2014-08-05 15:35:21 +04:00
}
2014-12-05 05:55:51 +03:00
v . aliases [ alias ] = key
2014-04-05 09:19:39 +04:00
}
} else {
2021-12-09 20:36:49 +03:00
v . logger . Warn ( "creating circular reference alias" , "alias" , alias , "key" , key , "real_key" , v . realKey ( key ) )
2014-04-05 09:19:39 +04:00
}
}
2015-02-17 17:22:37 +03:00
func ( v * Viper ) realKey ( key string ) string {
2014-12-05 05:55:51 +03:00
newkey , exists := v . aliases [ key ]
2014-04-05 09:19:39 +04:00
if exists {
2021-12-09 20:36:49 +03:00
v . logger . Debug ( "key is an alias" , "alias" , key , "to" , newkey )
2014-12-05 05:55:51 +03:00
return v . realKey ( newkey )
2014-04-05 09:19:39 +04:00
}
2016-09-20 11:17:41 +03:00
return key
2014-04-05 01:21:59 +04:00
}
2016-09-20 11:17:41 +03:00
// InConfig checks to see if the given key (or an alias) is in the config file.
2014-12-05 05:55:51 +03:00
func InConfig ( key string ) bool { return v . InConfig ( key ) }
2020-09-11 18:48:38 +03:00
2015-02-17 17:22:37 +03:00
func ( v * Viper ) InConfig ( key string ) bool {
2021-06-01 17:13:11 +03:00
lcaseKey := strings . ToLower ( key )
2014-04-05 09:19:39 +04:00
// if the requested key is an alias, then return the proper key
2021-06-01 17:13:11 +03:00
lcaseKey = v . realKey ( lcaseKey )
path := strings . Split ( lcaseKey , v . keyDelim )
2014-04-05 09:19:39 +04:00
2021-06-01 17:13:11 +03:00
return v . searchIndexableWithPathPrefixes ( v . config , path ) != nil
2014-04-05 01:21:59 +04:00
}
2016-09-20 11:17:41 +03:00
// SetDefault sets the default value for this key.
2016-10-30 00:33:52 +03:00
// SetDefault is case-insensitive for a key.
2014-07-11 18:42:07 +04:00
// Default only used when no value is provided by the user via flag, config or ENV.
2023-09-26 17:59:38 +03:00
func SetDefault ( key string , value any ) { v . SetDefault ( key , value ) }
2020-09-11 18:48:38 +03:00
2023-09-26 17:59:38 +03:00
func ( v * Viper ) SetDefault ( key string , value any ) {
2014-04-05 01:21:59 +04:00
// If alias passed in, then set the proper default
2014-12-05 05:55:51 +03:00
key = v . realKey ( strings . ToLower ( key ) )
2016-10-24 18:49:00 +03:00
value = toCaseInsensitiveValue ( value )
2016-10-08 11:00:18 +03:00
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-05 01:21:59 +04:00
}
2018-08-28 10:29:26 +03:00
// Set sets the value for the key in the override register.
2016-10-30 00:33:52 +03:00
// Set is case-insensitive for a key.
2014-10-24 23:38:01 +04:00
// Will be used instead of values obtained via
2016-09-20 11:17:41 +03:00
// flags, config file, ENV, default, or key/value store.
2023-09-26 17:59:38 +03:00
func Set ( key string , value any ) { v . Set ( key , value ) }
2020-09-11 18:48:38 +03:00
2023-09-26 17:59:38 +03:00
func ( v * Viper ) Set ( key string , value any ) {
2014-04-05 01:21:59 +04:00
// If alias passed in, then set the proper override
2014-12-05 05:55:51 +03:00
key = v . realKey ( strings . ToLower ( key ) )
2016-10-24 18:49:00 +03:00
value = toCaseInsensitiveValue ( value )
2016-10-08 11:00:18 +03:00
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
2014-04-09 02:57:45 +04:00
}
2016-09-20 11:17:41 +03:00
// ReadInConfig will discover and load the configuration file from disk
2014-10-24 23:38:01 +04:00
// and key/value stores, searching in one of the defined paths.
2014-12-05 05:55:51 +03:00
func ReadInConfig ( ) error { return v . ReadInConfig ( ) }
2020-09-11 18:48:38 +03:00
2015-02-17 17:22:37 +03:00
func ( v * Viper ) ReadInConfig ( ) error {
2021-12-09 20:36:49 +03:00
v . logger . Info ( "attempting to read in config file" )
2016-10-13 14:33:30 +03:00
filename , err := v . getConfigFile ( )
if err != nil {
return err
}
2014-12-05 05:55:51 +03:00
if ! stringInSlice ( v . getConfigType ( ) , SupportedExts ) {
return UnsupportedConfigError ( v . getConfigType ( ) )
2014-04-05 01:21:59 +04:00
}
2021-12-09 20:36:49 +03:00
v . logger . Debug ( "reading file" , "file" , filename )
2016-10-13 14:33:30 +03:00
file , err := afero . ReadFile ( v . fs , filename )
2014-04-09 02:57:45 +04:00
if err != nil {
return err
2014-04-05 01:21:59 +04:00
}
2014-04-09 02:57:45 +04:00
2023-09-26 17:59:38 +03:00
config := make ( map [ string ] any )
2016-12-28 06:49:59 +03:00
err = v . unmarshalReader ( bytes . NewReader ( file ) , config )
if err != nil {
return err
}
2015-04-26 22:08:10 +03:00
2016-12-28 06:49:59 +03:00
v . config = config
return nil
2014-04-05 01:21:59 +04:00
}
2014-12-05 05:55:51 +03:00
2015-11-12 23:20:40 +03:00
// MergeInConfig merges a new configuration with an existing config.
func MergeInConfig ( ) error { return v . MergeInConfig ( ) }
2020-09-11 18:48:38 +03:00
2015-11-12 23:20:40 +03:00
func ( v * Viper ) MergeInConfig ( ) error {
2021-12-09 20:36:49 +03:00
v . logger . Info ( "attempting to merge in config file" )
2016-10-13 14:33:30 +03:00
filename , err := v . getConfigFile ( )
if err != nil {
return err
}
2016-12-13 12:38:49 +03:00
if ! stringInSlice ( v . getConfigType ( ) , SupportedExts ) {
return UnsupportedConfigError ( v . getConfigType ( ) )
}
2016-10-13 14:33:30 +03:00
file , err := afero . ReadFile ( v . fs , filename )
2015-11-12 23:20:40 +03:00
if err != nil {
return err
}
return v . MergeConfig ( bytes . NewReader ( file ) )
}
2016-09-20 11:17:41 +03:00
// ReadConfig will read a configuration file, setting existing keys to nil if the
2015-11-12 23:20:40 +03:00
// key does not exist in the file.
2015-05-14 12:40:59 +03:00
func ReadConfig ( in io . Reader ) error { return v . ReadConfig ( in ) }
2020-09-11 18:48:38 +03:00
2015-05-14 12:40:59 +03:00
func ( v * Viper ) ReadConfig ( in io . Reader ) error {
2023-09-26 17:59:38 +03:00
v . config = make ( map [ string ] any )
2015-08-24 06:40:56 +03:00
return v . unmarshalReader ( in , v . config )
2015-05-08 12:13:33 +03:00
}
2015-11-12 23:20:40 +03:00
// MergeConfig merges a new configuration with an existing config.
func MergeConfig ( in io . Reader ) error { return v . MergeConfig ( in ) }
2020-09-11 18:48:38 +03:00
2015-11-12 23:20:40 +03:00
func ( v * Viper ) MergeConfig ( in io . Reader ) error {
2023-09-26 17:59:38 +03:00
cfg := make ( map [ string ] any )
2015-11-12 23:20:40 +03:00
if err := v . unmarshalReader ( in , cfg ) ; err != nil {
return err
}
2018-12-05 17:19:25 +03:00
return v . MergeConfigMap ( cfg )
}
// MergeConfigMap merges the configuration from the map given with an existing config.
2018-12-07 13:02:11 +03:00
// Note that the map given may be modified.
2023-09-26 17:59:38 +03:00
func MergeConfigMap ( cfg map [ string ] any ) error { return v . MergeConfigMap ( cfg ) }
2020-09-11 18:48:38 +03:00
2023-09-26 17:59:38 +03:00
func ( v * Viper ) MergeConfigMap ( cfg map [ string ] any ) error {
2018-12-05 17:19:25 +03:00
if v . config == nil {
2023-09-26 17:59:38 +03:00
v . config = make ( map [ string ] any )
2018-12-05 17:19:25 +03:00
}
2018-12-07 13:02:11 +03:00
insensitiviseMap ( cfg )
2015-11-12 23:20:40 +03:00
mergeMaps ( cfg , v . config , nil )
return nil
}
2017-12-07 07:26:31 +03:00
// WriteConfig writes the current configuration to a file.
func WriteConfig ( ) error { return v . WriteConfig ( ) }
2020-09-11 18:48:38 +03:00
2017-12-07 07:26:31 +03:00
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-11 18:48:38 +03:00
2017-12-07 07:26:31 +03:00
func ( v * Viper ) SafeWriteConfig ( ) error {
2019-09-13 17:29:19 +03:00
if len ( v . configPaths ) < 1 {
2019-12-06 14:46:23 +03:00
return errors . New ( "missing configuration for 'configPath'" )
2017-12-07 07:26:31 +03:00
}
2019-09-13 17:29:19 +03:00
return v . SafeWriteConfigAs ( filepath . Join ( v . configPaths [ 0 ] , v . configName + "." + v . configType ) )
2017-12-07 07:26:31 +03:00
}
// WriteConfigAs writes current configuration to a given filename.
func WriteConfigAs ( filename string ) error { return v . WriteConfigAs ( filename ) }
2020-09-11 18:48:38 +03:00
2017-12-07 07:26:31 +03:00
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-11 18:48:38 +03:00
2017-12-07 07:26:31 +03:00
func ( v * Viper ) SafeWriteConfigAs ( filename string ) error {
2019-12-04 04:14:08 +03:00
alreadyExists , err := afero . Exists ( v . fs , filename )
if alreadyExists && err == nil {
2019-09-13 17:29:19 +03:00
return ConfigFileAlreadyExistsError ( filename )
}
2017-12-07 07:26:31 +03:00
return v . writeConfig ( filename , false )
}
func ( v * Viper ) writeConfig ( filename string , force bool ) error {
2021-12-09 20:36:49 +03:00
v . logger . Info ( "attempting to write configuration to file" )
2020-02-20 02:41:04 +03:00
var configType string
2017-12-07 07:26:31 +03:00
ext := filepath . Ext ( filename )
2021-01-15 16:19:47 +03:00
if ext != "" && ext != filepath . Base ( filename ) {
2020-02-20 02:41:04 +03:00
configType = ext [ 1 : ]
} else {
configType = v . configType
2017-12-07 07:26:31 +03:00
}
2020-02-20 02:41:04 +03:00
if configType == "" {
return fmt . Errorf ( "config type could not be determined for %s" , filename )
}
2017-12-07 07:26:31 +03:00
if ! stringInSlice ( configType , SupportedExts ) {
return UnsupportedConfigError ( configType )
}
if v . config == nil {
2023-09-26 17:59:38 +03:00
v . config = make ( map [ string ] any )
2017-12-07 07:26:31 +03:00
}
2018-01-30 13:15:47 +03:00
flags := os . O_CREATE | os . O_TRUNC | os . O_WRONLY
if ! force {
flags |= os . O_EXCL
2017-12-07 07:26:31 +03:00
}
2019-02-22 21:54:48 +03:00
f , err := v . fs . OpenFile ( filename , flags , v . configPermissions )
2017-12-07 07:26:31 +03:00
if err != nil {
return err
}
2018-07-27 11:10:14 +03:00
defer f . Close ( )
if err := v . marshalWriter ( f , configType ) ; err != nil {
return err
}
return f . Sync ( )
2017-12-07 07:26:31 +03:00
}
// Unmarshal a Reader into a map.
// Should probably be an unexported function.
2023-09-26 17:59:38 +03:00
func unmarshalReader ( in io . Reader , c map [ string ] any ) error {
2017-12-07 07:26:31 +03:00
return v . unmarshalReader ( in , c )
}
2020-09-11 18:48:38 +03:00
2023-09-26 17:59:38 +03:00
func ( v * Viper ) unmarshalReader ( in io . Reader , c map [ string ] any ) error {
2017-12-07 07:26:31 +03:00
buf := new ( bytes . Buffer )
buf . ReadFrom ( in )
2020-09-23 18:46:17 +03:00
switch format := strings . ToLower ( v . getConfigType ( ) ) ; format {
2021-07-20 02:46:45 +03:00
case "yaml" , "yml" , "json" , "toml" , "hcl" , "tfvars" , "ini" , "properties" , "props" , "prop" , "dotenv" , "env" :
2021-07-16 04:09:43 +03:00
err := v . decoderRegistry . Decode ( format , buf . Bytes ( ) , c )
2017-12-07 07:26:31 +03:00
if err != nil {
return ConfigParseError { err }
}
}
insensitiviseMap ( c )
return nil
}
// Marshal a map into Writer.
func ( v * Viper ) marshalWriter ( f afero . File , configType string ) error {
c := v . AllSettings ( )
switch configType {
2021-07-20 02:46:45 +03:00
case "yaml" , "yml" , "json" , "toml" , "hcl" , "tfvars" , "ini" , "prop" , "props" , "properties" , "dotenv" , "env" :
2021-07-16 04:09:43 +03:00
b , err := v . encoderRegistry . Encode ( configType , c )
2017-12-07 07:26:31 +03:00
if err != nil {
return ConfigMarshalError { err }
}
2020-09-23 18:46:17 +03:00
_ , err = f . WriteString ( string ( b ) )
2017-12-07 07:26:31 +03:00
if err != nil {
return ConfigMarshalError { err }
}
}
return nil
}
2023-09-26 17:59:38 +03:00
func keyExists ( k string , m map [ string ] any ) string {
2015-11-12 23:20:40 +03:00
lk := strings . ToLower ( k )
for mk := range m {
lmk := strings . ToLower ( mk )
if lmk == lk {
return mk
}
}
return ""
}
func castToMapStringInterface (
2023-09-26 17:59:38 +03:00
src map [ any ] any ,
) map [ string ] any {
tgt := map [ string ] any { }
2015-11-12 23:20:40 +03:00
for k , v := range src {
tgt [ fmt . Sprintf ( "%v" , k ) ] = v
}
return tgt
}
2023-09-26 17:59:38 +03:00
func castMapStringSliceToMapInterface ( src map [ string ] [ ] string ) map [ string ] any {
tgt := map [ string ] any { }
2020-09-10 13:08:26 +03:00
for k , v := range src {
tgt [ k ] = v
}
return tgt
}
2023-09-26 17:59:38 +03:00
func castMapStringToMapInterface ( src map [ string ] string ) map [ string ] any {
tgt := map [ string ] any { }
2016-10-08 11:00:18 +03:00
for k , v := range src {
tgt [ k ] = v
}
return tgt
}
2023-09-26 17:59:38 +03:00
func castMapFlagToMapInterface ( src map [ string ] FlagValue ) map [ string ] any {
tgt := map [ string ] any { }
2016-10-24 00:04:21 +03:00
for k , v := range src {
tgt [ k ] = v
}
return tgt
}
2015-11-12 23:20:40 +03:00
// mergeMaps merges two maps. The `itgt` parameter is for handling go-yaml's
2023-09-26 17:59:38 +03:00
// insistence on parsing nested structures as `map[any]any`
2015-11-12 23:20:40 +03:00
// 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
2023-09-26 17:59:38 +03:00
// `map[string]any` instead.
func mergeMaps ( src , tgt map [ string ] any , itgt map [ any ] any ) {
2015-11-12 23:20:40 +03:00
for sk , sv := range src {
tk := keyExists ( sk , tgt )
if tk == "" {
2023-09-12 01:03:41 +03:00
v . logger . Debug ( "" , "tk" , "\"\"" , fmt . Sprintf ( "tgt[%s]" , sk ) , sv )
2015-11-12 23:20:40 +03:00
tgt [ sk ] = sv
if itgt != nil {
itgt [ sk ] = sv
}
continue
}
tv , ok := tgt [ tk ]
if ! ok {
2023-09-12 01:03:41 +03:00
v . logger . Debug ( "" , fmt . Sprintf ( "ok[%s]" , tk ) , false , fmt . Sprintf ( "tgt[%s]" , sk ) , sv )
2015-11-12 23:20:40 +03:00
tgt [ sk ] = sv
if itgt != nil {
itgt [ sk ] = sv
}
continue
}
svType := reflect . TypeOf ( sv )
tvType := reflect . TypeOf ( tv )
2023-09-12 01:03:41 +03:00
v . logger . Debug (
2021-12-09 20:36:49 +03:00
"processing" ,
"key" , sk ,
"st" , svType ,
"tt" , tvType ,
"sv" , sv ,
"tv" , tv ,
)
2015-11-12 23:20:40 +03:00
switch ttv := tv . ( type ) {
2023-09-26 17:59:38 +03:00
case map [ any ] any :
2023-09-12 01:03:41 +03:00
v . logger . Debug ( "merging maps (must convert)" )
2023-09-26 17:59:38 +03:00
tsv , ok := sv . ( map [ any ] any )
2020-12-23 16:28:47 +03:00
if ! ok {
v . logger . Error (
2023-09-26 17:59:38 +03:00
"Could not cast sv to map[any]any" ,
2022-05-05 20:50:15 +03:00
"key" , sk ,
"st" , svType ,
"tt" , tvType ,
"sv" , sv ,
"tv" , tv ,
)
2020-12-23 16:28:47 +03:00
continue
}
2015-11-12 23:20:40 +03:00
ssv := castToMapStringInterface ( tsv )
stv := castToMapStringInterface ( ttv )
mergeMaps ( ssv , stv , ttv )
2023-09-26 17:59:38 +03:00
case map [ string ] any :
2023-09-12 01:03:41 +03:00
v . logger . Debug ( "merging maps" )
2023-09-26 17:59:38 +03:00
tsv , ok := sv . ( map [ string ] any )
2020-12-23 16:28:47 +03:00
if ! ok {
2021-10-14 16:29:39 +03:00
v . logger . Error (
2023-09-26 17:59:38 +03:00
"Could not cast sv to map[string]any" ,
2022-05-05 20:50:15 +03:00
"key" , sk ,
"st" , svType ,
"tt" , tvType ,
"sv" , sv ,
"tv" , tv ,
)
2020-12-23 16:28:47 +03:00
continue
}
mergeMaps ( tsv , ttv , nil )
2015-11-12 23:20:40 +03:00
default :
2023-09-12 01:03:41 +03:00
v . logger . Debug ( "setting value" )
2015-11-12 23:20:40 +03:00
tgt [ tk ] = sv
if itgt != nil {
itgt [ tk ] = sv
}
}
}
}
2016-09-20 11:17:41 +03:00
// ReadRemoteConfig attempts to get configuration from a remote source
2015-04-02 00:08:42 +03:00
// and read it in the remote configuration registry.
2014-12-05 05:55:51 +03:00
func ReadRemoteConfig ( ) error { return v . ReadRemoteConfig ( ) }
2020-09-11 18:48:38 +03:00
2015-02-17 17:22:37 +03:00
func ( v * Viper ) ReadRemoteConfig ( ) error {
2016-09-20 11:17:41 +03:00
return v . getKeyValueConfig ( )
2014-10-27 17:14:45 +03:00
}
2014-12-05 05:55:51 +03:00
2015-05-08 12:13:33 +03:00
func WatchRemoteConfig ( ) error { return v . WatchRemoteConfig ( ) }
func ( v * Viper ) WatchRemoteConfig ( ) error {
2016-09-20 11:17:41 +03:00
return v . watchKeyValueConfig ( )
2015-05-08 12:13:33 +03:00
}
2017-03-15 16:43:09 +03:00
func ( v * Viper ) WatchRemoteConfigOnChannel ( ) error {
return v . watchKeyValueConfigOnChannel ( )
}
2016-09-20 11:17:41 +03:00
// Retrieve the first found remote configuration.
2015-02-17 17:22:37 +03:00
func ( v * Viper ) getKeyValueConfig ( ) error {
2015-05-30 22:28:33 +03:00
if RemoteConfig == nil {
return RemoteConfigError ( "Enable the remote features by doing a blank import of the viper/remote package: '_ github.com/spf13/viper/remote'" )
}
2022-06-11 14:14:10 +03:00
if len ( v . remoteProviders ) == 0 {
return RemoteConfigError ( "No Remote Providers" )
}
2014-12-05 05:55:51 +03:00
for _ , rp := range v . remoteProviders {
val , err := v . getRemoteConfig ( rp )
2014-10-24 23:38:01 +04:00
if err != nil {
2021-12-09 20:36:49 +03:00
v . logger . Error ( fmt . Errorf ( "get remote config: %w" , err ) . Error ( ) )
2020-09-09 23:35:18 +03:00
2014-10-26 16:42:03 +03:00
continue
2014-10-24 23:38:01 +04:00
}
2020-09-09 23:35:18 +03:00
2014-12-05 05:55:51 +03:00
v . kvstore = val
2020-09-09 23:35:18 +03:00
2014-10-26 16:42:03 +03:00
return nil
2014-10-24 23:38:01 +04:00
}
2014-10-27 18:03:11 +03:00
return RemoteConfigError ( "No Files Found" )
}
2023-09-26 17:59:38 +03:00
func ( v * Viper ) getRemoteConfig ( provider RemoteProvider ) ( map [ string ] any , error ) {
2015-05-30 22:28:33 +03:00
reader , err := RemoteConfig . Get ( provider )
2014-10-26 16:42:03 +03:00
if err != nil {
return nil , err
}
2015-08-24 06:40:56 +03:00
err = v . unmarshalReader ( reader , v . kvstore )
2014-12-05 05:55:51 +03:00
return v . kvstore , err
2015-05-08 12:13:33 +03:00
}
2017-03-15 16:43:09 +03:00
// Retrieve the first found remote configuration.
func ( v * Viper ) watchKeyValueConfigOnChannel ( ) error {
2022-06-11 14:14:10 +03:00
if len ( v . remoteProviders ) == 0 {
return RemoteConfigError ( "No Remote Providers" )
}
2017-03-15 16:43:09 +03:00
for _ , rp := range v . remoteProviders {
respc , _ := RemoteConfig . WatchChannel ( rp )
2019-11-06 14:40:41 +03:00
// Todo: Add quit channel
2017-03-15 16:43:09 +03:00
go func ( rc <- chan * RemoteResponse ) {
for {
b := <- rc
reader := bytes . NewReader ( b . Value )
v . unmarshalReader ( reader , v . kvstore )
}
} ( respc )
return nil
}
return RemoteConfigError ( "No Files Found" )
}
2016-09-20 11:17:41 +03:00
// Retrieve the first found remote configuration.
2015-05-08 12:13:33 +03:00
func ( v * Viper ) watchKeyValueConfig ( ) error {
2022-06-11 14:14:10 +03:00
if len ( v . remoteProviders ) == 0 {
return RemoteConfigError ( "No Remote Providers" )
}
2015-05-08 12:13:33 +03:00
for _ , rp := range v . remoteProviders {
val , err := v . watchRemoteConfig ( rp )
if err != nil {
2022-06-11 14:14:10 +03:00
v . logger . Error ( fmt . Errorf ( "watch remote config: %w" , err ) . Error ( ) )
2015-05-08 12:13:33 +03:00
continue
}
v . kvstore = val
return nil
}
return RemoteConfigError ( "No Files Found" )
}
2023-09-26 17:59:38 +03:00
func ( v * Viper ) watchRemoteConfig ( provider RemoteProvider ) ( map [ string ] any , error ) {
2015-05-30 22:28:33 +03:00
reader , err := RemoteConfig . Watch ( provider )
2015-05-08 12:13:33 +03:00
if err != nil {
return nil , err
}
2015-08-24 06:40:56 +03:00
err = v . unmarshalReader ( reader , v . kvstore )
2015-05-08 12:13:33 +03:00
return v . kvstore , err
2014-10-26 16:42:03 +03:00
}
2016-10-08 11:00:18 +03:00
// AllKeys returns all keys holding a value, regardless of where they are set.
2023-10-09 17:52:53 +03:00
// Nested keys are returned with a v.keyDelim separator.
2014-12-05 05:55:51 +03:00
func AllKeys ( ) [ ] string { return v . AllKeys ( ) }
2020-09-11 18:48:38 +03:00
2015-02-17 17:22:37 +03:00
func ( v * Viper ) AllKeys ( ) [ ] string {
2016-10-08 11:00:18 +03:00
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 , "" )
2016-10-24 00:04:21 +03:00
m = v . mergeFlatMap ( m , castMapFlagToMapInterface ( v . pflags ) )
2020-09-10 13:08:26 +03:00
m = v . mergeFlatMap ( m , castMapStringSliceToMapInterface ( v . env ) )
2016-10-08 11:00:18 +03:00
m = v . flattenAndMergeMap ( m , v . config , "" )
m = v . flattenAndMergeMap ( m , v . kvstore , "" )
m = v . flattenAndMergeMap ( m , v . defaults , "" )
// convert set of paths to list
2019-10-09 16:36:40 +03:00
a := make ( [ ] string , 0 , len ( m ) )
2016-10-08 11:00:18 +03:00
for x := range m {
a = append ( a , x )
2014-09-28 01:00:51 +04:00
}
2016-10-08 11:00:18 +03:00
return a
}
2014-09-28 01:00:51 +04:00
2016-10-08 11:00:18 +03:00
// 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-03 19:05:45 +03: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.
//
2016-10-08 11:00:18 +03:00
// The resulting set of paths is merged to the given shadow set at the same time.
2023-09-26 17:59:38 +03:00
func ( v * Viper ) flattenAndMergeMap ( shadow map [ string ] bool , m map [ string ] any , prefix string ) map [ string ] bool {
2016-10-08 11:00:18 +03:00
if shadow != nil && prefix != "" && shadow [ prefix ] {
// prefix is shadowed => nothing more to flatten
return shadow
2014-10-24 23:38:01 +04:00
}
2016-10-08 11:00:18 +03:00
if shadow == nil {
shadow = make ( map [ string ] bool )
2014-09-28 01:00:51 +04:00
}
2023-09-26 17:59:38 +03:00
var m2 map [ string ] any
2016-10-08 11:00:18 +03:00
if prefix != "" {
prefix += v . keyDelim
2016-01-21 00:15:43 +03:00
}
2016-10-08 11:00:18 +03:00
for k , val := range m {
fullKey := prefix + k
2023-07-27 21:56:32 +03:00
switch val := val . ( type ) {
2023-09-26 17:59:38 +03:00
case map [ string ] any :
2023-07-27 21:56:32 +03:00
m2 = val
2023-09-26 17:59:38 +03:00
case map [ any ] any :
2016-10-08 11:00:18 +03:00
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.
2023-09-26 17:59:38 +03:00
func ( v * Viper ) mergeFlatMap ( shadow map [ string ] bool , m map [ string ] any ) map [ string ] bool {
2016-10-08 11:00:18 +03:00
// scan keys
outer :
2019-07-20 01:07:23 +03:00
for k := range m {
2016-10-08 11:00:18 +03:00
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
2014-09-28 01:00:51 +04:00
}
2016-10-08 11:00:18 +03:00
return shadow
2014-09-28 01:00:51 +04:00
}
2023-09-26 17:59:38 +03:00
// AllSettings merges all settings and returns them as a map[string]any.
func AllSettings ( ) map [ string ] any { return v . AllSettings ( ) }
2020-09-11 18:48:38 +03:00
2023-09-26 17:59:38 +03:00
func ( v * Viper ) AllSettings ( ) map [ string ] any {
2023-12-05 17:17:54 +03:00
return v . getSettings ( v . AllKeys ( ) )
}
func ( v * Viper ) getSettings ( keys [ ] string ) map [ string ] any {
2023-09-26 17:59:38 +03:00
m := map [ string ] any { }
2016-10-08 11:00:18 +03:00
// start from the list of keys, and construct the map one value at a time
2023-12-05 17:17:54 +03:00
for _ , k := range keys {
2016-10-08 11:00:18 +03:00
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
}
2019-09-27 18:18:11 +03:00
path := strings . Split ( k , v . keyDelim )
2016-10-08 11:00:18 +03:00
lastKey := strings . ToLower ( path [ len ( path ) - 1 ] )
deepestMap := deepSearch ( m , path [ 0 : len ( path ) - 1 ] )
// set innermost value
deepestMap [ lastKey ] = value
2014-09-28 01:00:51 +04:00
}
return m
}
2016-09-20 11:17:41 +03:00
// SetFs sets the filesystem to use to read configuration.
2016-08-05 10:45:58 +03:00
func SetFs ( fs afero . Fs ) { v . SetFs ( fs ) }
2020-09-11 18:48:38 +03:00
2016-08-05 10:45:58 +03:00
func ( v * Viper ) SetFs ( fs afero . Fs ) {
v . fs = fs
}
2016-09-20 11:17:41 +03:00
// SetConfigName sets name for the config file.
2014-07-11 18:42:07 +04:00
// Does not include extension.
2014-12-05 05:55:51 +03:00
func SetConfigName ( in string ) { v . SetConfigName ( in ) }
2020-09-11 18:48:38 +03:00
2015-02-17 17:22:37 +03:00
func ( v * Viper ) SetConfigName ( in string ) {
2014-04-05 01:21:59 +04:00
if in != "" {
2014-12-05 05:55:51 +03:00
v . configName = in
2016-08-05 10:18:19 +03:00
v . configFile = ""
2014-04-05 01:21:59 +04:00
}
}
2016-09-20 11:17:41 +03:00
// SetConfigType sets the type of the configuration returned by the
2015-04-02 00:08:42 +03:00
// remote source, e.g. "json".
2014-12-05 05:55:51 +03:00
func SetConfigType ( in string ) { v . SetConfigType ( in ) }
2020-09-11 18:48:38 +03:00
2015-02-17 17:22:37 +03:00
func ( v * Viper ) SetConfigType ( in string ) {
2014-04-05 01:21:59 +04:00
if in != "" {
2014-12-05 05:55:51 +03:00
v . configType = in
2014-04-05 01:21:59 +04:00
}
}
2019-02-22 21:54:48 +03:00
// SetConfigPermissions sets the permissions for the config file.
func SetConfigPermissions ( perm os . FileMode ) { v . SetConfigPermissions ( perm ) }
2020-09-11 18:48:38 +03:00
2019-02-22 21:54:48 +03:00
func ( v * Viper ) SetConfigPermissions ( perm os . FileMode ) {
v . configPermissions = perm . Perm ( )
}
2021-01-26 22:18:05 +03:00
// IniLoadOptions sets the load options for ini parsing.
func IniLoadOptions ( in ini . LoadOptions ) Option {
return optionFunc ( func ( v * Viper ) {
v . iniLoadOptions = in
} )
}
2015-02-17 17:22:37 +03:00
func ( v * Viper ) getConfigType ( ) string {
2014-12-05 05:55:51 +03:00
if v . configType != "" {
return v . configType
2014-04-05 01:21:59 +04:00
}
2016-10-13 14:33:30 +03:00
cf , err := v . getConfigFile ( )
if err != nil {
return ""
}
2014-11-13 22:43:51 +03:00
ext := filepath . Ext ( cf )
2014-04-05 01:21:59 +04:00
if len ( ext ) > 1 {
return ext [ 1 : ]
}
2016-09-20 11:17:41 +03:00
return ""
2014-04-05 01:21:59 +04:00
}
2016-10-13 14:33:30 +03:00
func ( v * Viper ) getConfigFile ( ) ( string , error ) {
2018-03-19 21:12:24 +03:00
if v . configFile == "" {
cf , err := v . findConfigFile ( )
if err != nil {
return "" , err
}
v . configFile = cf
2014-04-05 01:21:59 +04:00
}
2018-03-19 21:12:24 +03:00
return v . configFile , nil
2014-04-05 01:21:59 +04:00
}
2016-09-20 11:17:41 +03:00
// Debug prints all configuration registries for debugging
2015-04-02 00:08:42 +03:00
// purposes.
2022-08-11 21:55:24 +03:00
func Debug ( ) { v . Debug ( ) }
func DebugTo ( w io . Writer ) { v . DebugTo ( w ) }
func ( v * Viper ) Debug ( ) { v . DebugTo ( os . Stdout ) }
func ( v * Viper ) DebugTo ( w io . Writer ) {
fmt . Fprintf ( w , "Aliases:\n%#v\n" , v . aliases )
fmt . Fprintf ( w , "Override:\n%#v\n" , v . override )
fmt . Fprintf ( w , "PFlags:\n%#v\n" , v . pflags )
fmt . Fprintf ( w , "Env:\n%#v\n" , v . env )
fmt . Fprintf ( w , "Key/Value Store:\n%#v\n" , v . kvstore )
fmt . Fprintf ( w , "Config:\n%#v\n" , v . config )
fmt . Fprintf ( w , "Defaults:\n%#v\n" , v . defaults )
2014-04-05 01:21:59 +04:00
}