Skip to content

Commit

Permalink
add autonaming (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
cristaloleg authored Sep 17, 2020
1 parent e7dfd86 commit 6399e4a
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 33 deletions.
95 changes: 83 additions & 12 deletions aconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"strconv"
"strings"
"time"
"unicode"

"github.com/BurntSushi/toml"
"gopkg.in/yaml.v2"
Expand Down Expand Up @@ -305,19 +306,11 @@ func (l *Loader) assertBuilt() {
}

func (l *Loader) getEnvName(field *fieldData) string {
name := field.name
if field.envName != "" {
name = field.envName
}
return strings.ToUpper(l.config.EnvPrefix + strings.ReplaceAll(name, ".", "_"))
return l.config.EnvPrefix + field.envName
}

func (l *Loader) getFlagName(field *fieldData) string {
name := field.name
if field.flagName != "" {
name = field.flagName
}
return strings.ToLower(l.config.FlagPrefix + name)
return l.config.FlagPrefix + field.flagName
}

func (l *Loader) setFieldData(field *fieldData, value string) error {
Expand Down Expand Up @@ -380,14 +373,16 @@ type fieldData struct {
}

func newFieldData(field reflect.StructField, value reflect.Value, parent *fieldData) *fieldData {
words := splitNameByWords(field.Name)

return &fieldData{
name: makeName(field.Name, parent),
parent: parent,
value: value,
field: field,
defaultValue: field.Tag.Get(defaultValueTag),
envName: field.Tag.Get(envNameTag),
flagName: field.Tag.Get(flagNameTag),
envName: makeEnvName(field, parent, words),
flagName: makeFlagName(field, parent, words),
usage: field.Tag.Get(usageTag),
}
}
Expand All @@ -403,6 +398,39 @@ func makeName(name string, parent *fieldData) string {
return parent.name + "." + name
}

func makeEnvName(field reflect.StructField, parent *fieldData, words []string) string {
envName := field.Tag.Get(envNameTag)
if envName == "" {
envName = makeParsingName(words)
}
if parent != nil {
envName = parent.envName + "_" + envName
}
return strings.ToUpper(envName)
}

func makeFlagName(field reflect.StructField, parent *fieldData, words []string) string {
flagName := field.Tag.Get(flagNameTag)
if flagName == "" {
flagName = makeParsingName(words)
}
if parent != nil {
flagName = parent.flagName + "." + flagName
}
return strings.ToLower(flagName)
}

func makeParsingName(words []string) string {
name := ""
for i, w := range words {
if i > 0 {
name += "_"
}
name += strings.ToLower(w)
}
return name
}

func (f *fieldData) Name() string {
return f.name
}
Expand Down Expand Up @@ -560,3 +588,46 @@ func setMap(field *fieldData, value string) error {
field.value.Set(mapField)
return nil
}

// based on https://github.com/fatih/camelcase
func splitNameByWords(src string) []string {
var runes [][]rune
lastClass, class := 0, 0

// split into fields based on class of unicode character
for _, r := range src {
switch true {
case unicode.IsLower(r):
class = 1
case unicode.IsUpper(r):
class = 2
case unicode.IsDigit(r):
class = 3
default:
class = 4
}
if class == lastClass {
runes[len(runes)-1] = append(runes[len(runes)-1], r)
} else {
runes = append(runes, []rune{r})
}
lastClass = class
}

// handle upper case -> lower case sequences, e.g.
// "PDFL", "oader" -> "PDF", "Loader"
for i := 0; i < len(runes)-1; i++ {
if unicode.IsUpper(runes[i][0]) && unicode.IsLower(runes[i+1][0]) {
runes[i+1] = append([]rune{runes[i][len(runes[i])-1]}, runes[i+1]...)
runes[i] = runes[i][:len(runes[i])-1]
}
}

words := []string{}
for _, s := range runes {
if len(s) > 0 {
words = append(words, string(s))
}
}
return words
}
14 changes: 7 additions & 7 deletions aconfig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,17 +221,17 @@ func TestLoadFile_WithFiles(t *testing.T) {
func TestLoadEnv(t *testing.T) {
setEnv(t, "TST_STR", "str-env")
setEnv(t, "TST_INT", "121")
setEnv(t, "TST_HTTPPORT", "3000")
setEnv(t, "TST_HTTP_PORT", "3000")
setEnv(t, "TST_SUB_FLOAT", "222.333")
setEnv(t, "TST_ANON_ISANON", "true")
setEnv(t, "TST_ANON_IS_ANON", "true")
setEnv(t, "TST_EM", "em-env")
defer os.Clearenv()

loader := LoaderFor(&TestConfig{}).
SkipDefaults().
SkipFiles().
SkipFlags().
WithEnvPrefix("tst").
WithEnvPrefix("TST").
Build()

var cfg TestConfig
Expand Down Expand Up @@ -259,9 +259,9 @@ func TestLoadFlag(t *testing.T) {
"-tst.str=str-flag",
"-tst.int=1001",
"-tst.int=1001",
"-tst.httpport=30000",
"-tst.http_port=30000",
"-tst.sub.float=123.321",
"-tst.anon.isanon=true",
"-tst.anon.is_anon=true",
"-tst.em=em-flag",
}

Expand Down Expand Up @@ -448,14 +448,14 @@ func TestBadFiles(t *testing.T) {
}

func TestBadEnvs(t *testing.T) {
setEnv(t, "TST_HTTPPORT", "30a00")
setEnv(t, "TST_HTTP_PORT", "30a00")
defer os.Clearenv()

loader := LoaderFor(&TestConfig{}).
SkipDefaults().
SkipFiles().
SkipFlags().
WithEnvPrefix("tst").
WithEnvPrefix("TST").
Build()

var cfg TestConfig
Expand Down
26 changes: 13 additions & 13 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import (
)

type MyConfig struct {
Port int `default:"1111"`
Auth struct {
HTTPPort int `default:"1111"`
Auth struct {
User string `default:"def-user"`
Pass string `default:"def-pass"`
}
Expand All @@ -29,13 +29,13 @@ func Example_NewApi() {
log.Panic(err)
}

fmt.Printf("Port: %v\n", cfg.Port)
fmt.Printf("HTTPPort: %v\n", cfg.HTTPPort)
fmt.Printf("Auth.User: %q\n", cfg.Auth.User)
fmt.Printf("Auth.Pass: %q\n", cfg.Auth.Pass)

// Output:
//
// Port: 0
// HTTPPort: 0
// Auth.User: ""
// Auth.Pass: ""
}
Expand All @@ -54,13 +54,13 @@ func Example_Defaults() {
log.Panic(err)
}

fmt.Printf("Port: %v\n", cfg.Port)
fmt.Printf("HTTPPort: %v\n", cfg.HTTPPort)
fmt.Printf("Auth.User: %v\n", cfg.Auth.User)
fmt.Printf("Auth.Pass: %v\n", cfg.Auth.Pass)

// Output:
//
// Port: 1111
// HTTPPort: 1111
// Auth.User: def-user
// Auth.Pass: def-pass
}
Expand All @@ -79,13 +79,13 @@ func Example_File() {
log.Panic(err)
}

fmt.Printf("Port: %v\n", cfg.Port)
fmt.Printf("HTTPPort: %v\n", cfg.HTTPPort)
fmt.Printf("Auth.User: %v\n", cfg.Auth.User)
fmt.Printf("Auth.Pass: %v\n", cfg.Auth.Pass)

// Output:
//
// Port: 2222
// HTTPPort: 2222
// Auth.User: json-user
// Auth.Pass: json-pass
}
Expand All @@ -94,7 +94,7 @@ func Example_File() {
// And then overwrite with environment variables.
//
func Example_Env() {
os.Setenv("EXAMPLE_PORT", "3333")
os.Setenv("EXAMPLE_HTTP_PORT", "3333")
os.Setenv("EXAMPLE_AUTH_USER", "env-user")
os.Setenv("EXAMPLE_AUTH_PASS", "env-pass")
defer os.Clearenv()
Expand All @@ -110,13 +110,13 @@ func Example_Env() {
log.Panic(err)
}

fmt.Printf("Port: %v\n", cfg.Port)
fmt.Printf("HTTPPort: %v\n", cfg.HTTPPort)
fmt.Printf("Auth.User: %v\n", cfg.Auth.User)
fmt.Printf("Auth.Pass: %v\n", cfg.Auth.Pass)

// Output:
//
// Port: 3333
// HTTPPort: 3333
// Auth.User: env-user
// Auth.Pass: env-pass
}
Expand All @@ -140,7 +140,7 @@ func Example_Flag() {
log.Panic(err)
}

fmt.Printf("Port: %v\n", cfg.Port)
fmt.Printf("HTTPPort: %v\n", cfg.HTTPPort)
fmt.Printf("Auth.User: %v\n", cfg.Auth.User)
fmt.Printf("Auth.Pass: %v\n", cfg.Auth.Pass)

Expand All @@ -150,7 +150,7 @@ func Example_Flag() {

// Output
//
// Port: 4444
// HTTPPort: 4444
// Auth.User: flag-user
// Auth.Pass: flag-pass
}
2 changes: 1 addition & 1 deletion testdata/example_config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"Port": 2222,
"HTTPPort": 2222,
"Auth": {
"User": "json-user",
"Pass": "json-pass"
Expand Down

0 comments on commit 6399e4a

Please sign in to comment.