mirror of
https://github.com/Luzifer/rconfig.git
synced 2024-11-09 16:30:04 +00:00
Add AutoEnv feature
to automatically derive environment variable names from struct field names Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
954ac1f137
commit
f2a0efe97c
3 changed files with 108 additions and 2 deletions
64
autoenv.go
Normal file
64
autoenv.go
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
package rconfig
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
type characterClass [2]rune
|
||||||
|
|
||||||
|
func (c characterClass) Contains(r rune) bool {
|
||||||
|
return c[0] <= r && c[1] >= r
|
||||||
|
}
|
||||||
|
|
||||||
|
type characterClasses []characterClass
|
||||||
|
|
||||||
|
func (c characterClasses) Contains(r rune) bool {
|
||||||
|
for _, cc := range c {
|
||||||
|
if cc.Contains(r) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
charGroupUpperLetter = characterClass{'A', 'Z'}
|
||||||
|
charGroupLowerLetter = characterClass{'a', 'z'}
|
||||||
|
charGroupNumber = characterClass{'0', '9'}
|
||||||
|
charGroupLowerNumber = characterClasses{charGroupLowerLetter, charGroupNumber}
|
||||||
|
)
|
||||||
|
|
||||||
|
func deriveEnvVarName(s string) string {
|
||||||
|
var (
|
||||||
|
words []string
|
||||||
|
word []rune
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, l := range s {
|
||||||
|
switch {
|
||||||
|
case charGroupUpperLetter.Contains(l):
|
||||||
|
if len(word) > 0 && charGroupLowerNumber.Contains(word[len(word)-1]) {
|
||||||
|
words = append(words, string(word))
|
||||||
|
word = []rune{}
|
||||||
|
}
|
||||||
|
word = append(word, l)
|
||||||
|
|
||||||
|
case charGroupLowerLetter.Contains(l):
|
||||||
|
if len(word) > 1 && charGroupUpperLetter.Contains(word[len(word)-1]) {
|
||||||
|
words = append(words, string(word[0:len(word)-1]))
|
||||||
|
word = word[len(word)-1:]
|
||||||
|
}
|
||||||
|
word = append(word, l)
|
||||||
|
|
||||||
|
case charGroupNumber.Contains(l):
|
||||||
|
word = append(word, l)
|
||||||
|
|
||||||
|
default:
|
||||||
|
if len(word) > 0 {
|
||||||
|
words = append(words, string(word))
|
||||||
|
}
|
||||||
|
word = []rune{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
words = append(words, string(word))
|
||||||
|
|
||||||
|
return strings.ToUpper(strings.Join(words, "_"))
|
||||||
|
}
|
29
autoenv_test.go
Normal file
29
autoenv_test.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package rconfig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDeriveEnvVarName(t *testing.T) {
|
||||||
|
for test, expect := range map[string]string{
|
||||||
|
"1Foobar": "1_FOOBAR",
|
||||||
|
"BC1": "BC1",
|
||||||
|
"BIGCase1": "BIG_CASE1",
|
||||||
|
"BIGCase": "BIG_CASE",
|
||||||
|
"Camel1": "CAMEL1",
|
||||||
|
"camel": "CAMEL",
|
||||||
|
"Camel": "CAMEL",
|
||||||
|
"CAMEL": "CAMEL",
|
||||||
|
"CamelCase": "CAMEL_CASE",
|
||||||
|
"_foobar": "FOOBAR",
|
||||||
|
"ILoveGoAndJSONSoMuch": "I_LOVE_GO_AND_JSON_SO_MUCH",
|
||||||
|
"mrT": "MR_T",
|
||||||
|
"my_case1": "MY_CASE1",
|
||||||
|
"MyFieldName": "MY_FIELD_NAME",
|
||||||
|
"SmallCASE": "SMALL_CASE",
|
||||||
|
} {
|
||||||
|
if d := deriveEnvVarName(test); d != expect {
|
||||||
|
t.Errorf("Derived variable %q did not match expectation %q", d, expect)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
config.go
17
config.go
|
@ -17,6 +17,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
autoEnv bool
|
||||||
fs *pflag.FlagSet
|
fs *pflag.FlagSet
|
||||||
variableDefaults map[string]string
|
variableDefaults map[string]string
|
||||||
)
|
)
|
||||||
|
@ -60,6 +61,13 @@ func Args() []string {
|
||||||
return fs.Args()
|
return fs.Args()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AutoEnv enables or disables automated env variable guessing. If no `env` struct
|
||||||
|
// tag was set and AutoEnv is enabled the env variable name is derived from the
|
||||||
|
// name of the field: `MyFieldName` will get `MY_FIELD_NAME`
|
||||||
|
func AutoEnv(enable bool) {
|
||||||
|
autoEnv = enable
|
||||||
|
}
|
||||||
|
|
||||||
// Usage prints a basic usage with the corresponding defaults for the flags to
|
// Usage prints a basic usage with the corresponding defaults for the flags to
|
||||||
// os.Stdout. The defaults are derived from the `default` struct-tag and the ENV.
|
// os.Stdout. The defaults are derived from the `default` struct-tag and the ENV.
|
||||||
func Usage() {
|
func Usage() {
|
||||||
|
@ -116,7 +124,7 @@ func execTags(in interface{}, fs *pflag.FlagSet) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
value := varDefault(typeField.Tag.Get("vardefault"), typeField.Tag.Get("default"))
|
value := varDefault(typeField.Tag.Get("vardefault"), typeField.Tag.Get("default"))
|
||||||
value = envDefault(typeField.Tag.Get("env"), value)
|
value = envDefault(typeField, value)
|
||||||
parts := strings.Split(typeField.Tag.Get("flag"), ",")
|
parts := strings.Split(typeField.Tag.Get("flag"), ",")
|
||||||
|
|
||||||
switch typeField.Type {
|
switch typeField.Type {
|
||||||
|
@ -334,9 +342,14 @@ func registerFlagUint(t reflect.Kind, fs *pflag.FlagSet, field interface{}, part
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func envDefault(env, def string) string {
|
func envDefault(field reflect.StructField, def string) string {
|
||||||
value := def
|
value := def
|
||||||
|
|
||||||
|
env := field.Tag.Get("env")
|
||||||
|
if env == "" && autoEnv {
|
||||||
|
env = deriveEnvVarName(field.Name)
|
||||||
|
}
|
||||||
|
|
||||||
if env != "" {
|
if env != "" {
|
||||||
if e := os.Getenv(env); e != "" {
|
if e := os.Getenv(env); e != "" {
|
||||||
value = e
|
value = e
|
||||||
|
|
Loading…
Reference in a new issue