Repository: spf13/viper (go)

Issue:
SetEnvKeyReplacer(): replace concrete type "strings.Replacer" with its interface
# Introduction
Current implementation of [SetEnvKeyReplacer](https://github.com/spf13/viper/blob/8e194e8ad2432506b0a6dac93dd7876fca7a94a5/viper.go#L1091) has parameter `r` with type of certain struct pointer: ***strings.Replacer**.
# Problem
The implementation doesn't allow us to use custom string-replacers. For example, we can't use **LowerCamelCase**-to-**ScreamingSnakeCase** replacer: `myParamter` -> `MY_PARAMETER`
# Solution
There's an internal interface in go's std-lib: [strings.replacer](https://golang.org/src/strings/replace.go). It is the good way to standardize all string-replacers.
If **SetEnvKeyReplacer()** accepts the interface, then we might use any implementation of it.
Could you change it, please?

Pull request description:
Add string replacer interface and env key replacer option
Fixes #569
Fixes #575
Fixes #624

Patch:
diff --git a/README.md b/README.md
index b0392c9..327308b 100644
--- a/README.md
+++ b/README.md
@@ -263,6 +263,9 @@ keys to an extent. This is useful if you want to use `-` or something in your
 `Get()` calls, but want your environmental variables to use `_` delimiters. An
 example of using it can be found in `viper_test.go`.
 
+Alternatively, you can use `EnvKeyReplacer` with `NewWithOptions` factory function.
+Unlike `SetEnvKeyReplacer`, it accepts a `StringReplacer` interface allowing you to write custom string replacing logic.
+
 By default empty environment variables are considered unset and will fall back to
 the next configuration source. To treat empty environment variables as set, use
 the `AllowEmptyEnv` method.
diff --git a/viper.go b/viper.go
index 4e4d6da..19f15d8 100644
--- a/viper.go
+++ b/viper.go
@@ -197,7 +197,7 @@ type Viper struct {
 	envPrefix         string
 
 	automaticEnvApplied bool
-	envKeyReplacer      *strings.Replacer
+	envKeyReplacer      StringReplacer
 	allowEmptyEnv       bool
 
 	config         map[string]interface{}
@@ -257,6 +257,19 @@ func KeyDelimiter(d string) Option {
 	})
 }
 
+// StringReplacer applies a set of replacements to a string.
+type StringReplacer interface {
+	// Replace returns a copy of s with all replacements performed.
+	Replace(s string) string
+}
+
+// EnvKeyReplacer sets a replacer used for mapping environment variables to internal keys.
+func EnvKeyReplacer(r StringReplacer) Option {
+	return optionFunc(func(v *Viper) {
+		v.envKeyReplacer = r
+	})
+}
+
 // NewWithOptions creates a new Viper instance.
 func NewWithOptions(opts ...Option) *Viper {
 	v := New()


Test patch:
diff --git a/viper_test.go b/viper_test.go
index 9f4dff3..8f010c7 100644
--- a/viper_test.go
+++ b/viper_test.go
@@ -549,6 +549,15 @@ func TestSetEnvKeyReplacer(t *testing.T) {
 	assert.Equal(t, "30s", Get("refresh-interval"))
 }
 
+func TestEnvKeyReplacer(t *testing.T) {
+	v := NewWithOptions(EnvKeyReplacer(strings.NewReplacer("-", "_")))
+
+	v.AutomaticEnv()
+	_ = os.Setenv("REFRESH_INTERVAL", "30s")
+
+	assert.Equal(t, "30s", v.Get("refresh-interval"))
+}
+
 func TestAllKeys(t *testing.T) {
 	initConfigs()