diff --git a/util.go b/util.go index 117c6ac..685428f 100644 --- a/util.go +++ b/util.go @@ -17,6 +17,7 @@ import ( "runtime" "strings" "unicode" + "regexp" slog "github.com/sagikazarmark/slog-shim" "github.com/spf13/cast" @@ -221,3 +222,17 @@ func deepSearch(m map[string]any, path []string) map[string]any { } return m } +// We look up a environmental variable if it looks like '${HOME}' +func lookupEnvByValue(s string) string { + match, err := regexp.MatchString("(\\$[A-Za-z_-]+|\\${[A-Za-z_-]+})", s) + if (err != nil) { + fmt.Println(match, err) + } + if (match) { + env := os.ExpandEnv(s) + if ( env != "" ) { + return env + } + } + return s +} diff --git a/viper.go b/viper.go index 20eb4da..474b7d2 100644 --- a/viper.go +++ b/viper.go @@ -978,6 +978,12 @@ func (v *Viper) GetString(key string) string { return cast.ToString(v.Get(key)) } +// GetStringEnv returns the value of the environmental variable associated with the key as a string. +func GetStringEnv(key string) string { return lookupEnvByValue(v.GetString(key)) } +func (v *Viper) GetStringEnv(key string) string { + return lookupEnvByValue(cast.ToString(v.Get(key))) +} + // GetBool returns the value associated with the key as a boolean. func GetBool(key string) bool { return v.GetBool(key) } diff --git a/viper_test.go b/viper_test.go index 0853369..7a1509c 100644 --- a/viper_test.go +++ b/viper_test.go @@ -48,6 +48,12 @@ import ( // beard: true // `) +var yamlExampleWithEnv = []byte(` +home: '${HOME_STUFF}' +undefined: '${UNDEFINED}' +theclown: ${HOME_STUFF} +`) + var yamlExampleWithExtras = []byte(`Existing: true Bogus: true `) @@ -708,6 +714,23 @@ func TestSetEnvKeyReplacer(t *testing.T) { assert.Equal(t, "30s", Get("refresh-interval")) } +func TestGetStringEnv(t *testing.T) { + Reset() + home := "." + os.Setenv("HOME_STUFF", home) + v := New() + v.SetConfigType("yaml") + v.ReadConfig(bytes.NewBuffer(yamlExampleWithEnv)) + value := v.GetStringEnv("home") + // defined env variables are interpolated + assert.Equal(t, ".", value) + value = v.GetStringEnv("theclown") + assert.Equal(t, ".", value) + // undefined env variables are untouched + undefined_value := v.GetStringEnv("undefined") + assert.Equal(t, "${UNDEFINED}", undefined_value) +} + func TestEnvKeyReplacer(t *testing.T) { v := NewWithOptions(EnvKeyReplacer(strings.NewReplacer("-", "_")))