Skip to content

Commit

Permalink
feat: Implement NameSep option
Browse files Browse the repository at this point in the history
  • Loading branch information
Taras Bunyk committed Apr 7, 2024
1 parent bc61971 commit 8b5126e
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 10 deletions.
37 changes: 33 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ fmt.Println(cfg.HTTP.Port) // 8080
A nested struct can have the optional `env:"PREFIX"` tag.
In this case, the environment variables declared by its fields are prefixed with PREFIX.
This rule is applied recursively to all nested structs.
Nested field names are separated by a string defined in `NameSep` option, empty string by default.

```go
os.Setenv("DB_HOST", "localhost")
Expand All @@ -100,14 +101,16 @@ var cfg struct {
DB struct {
Host string `env:"HOST"`
Port int `env:"PORT"`
} `env:"DB_"`
} `env:"DB"`
}
if err := env.Load(&cfg, nil); err != nil {
if err := env.Load(&cfg, &env.Options{
NameSep: "_",
}); err != nil {
fmt.Println(err)
}

fmt.Println(cfg.DB.Host) // localhost
fmt.Println(cfg.DB.Port) // 5432
fmt.Println(cfg.DB.Host)
fmt.Println(cfg.DB.Port)
```

### Default values
Expand Down Expand Up @@ -182,6 +185,32 @@ if err := env.Load(&cfg, &env.Options{SliceSep: ","}); err != nil {
fmt.Println(cfg.Ports) // [8080 8081 8082]
```

### Names separator
By defaul variable names of nested structs are built with just simple concatenation.

So given config like the following:

```go
var cfg struct {
DB struct {
Host string `env:"HOST"`
Port int `env:"PORT"`
} `env:"DB"`
}
```

would parse variables `DBHOST` and `DBPORT`.

This can be changed with `Options.NameSep`, so parsing it like

```go
env.Load(&cfg, &env.Options{NameSep: "_"})
```

Will parse `DB_HOST` and `DB_PORT` variable names.
Values like `"-"`, or any other string that builds valid environment variables
are also possible

### Source

By default, `Load` retrieves environment variables directly from OS.
Expand Down
10 changes: 7 additions & 3 deletions env.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
type Options struct {
Source Source // The source of environment variables. The default is [OS].
SliceSep string // The separator used to parse slice values. The default is space.
NameSep string // The separator used to join nested struct names. The default is empty string.
}

// NotSetError is returned when environment variables are marked as required but not set.
Expand Down Expand Up @@ -76,7 +77,7 @@ func Load(cfg any, opts *Options) error {
}

v := pv.Elem()
vars := parseVars(v)
vars := parseVars(v, opts)
cache[v.Type()] = vars

var notset []string
Expand Down Expand Up @@ -111,7 +112,7 @@ func Load(cfg any, opts *Options) error {
return nil
}

func parseVars(v reflect.Value) []Var {
func parseVars(v reflect.Value, opts *Options) []Var {
var vars []Var

for i := 0; i < v.NumField(); i++ {
Expand All @@ -128,7 +129,10 @@ func parseVars(v reflect.Value) []Var {
if ok {
prefix = value
}
for _, v := range parseVars(field) {
if opts != nil {
prefix += opts.NameSep
}
for _, v := range parseVars(field, opts) {
v.Name = prefix + v.Name
vars = append(vars, v)
}
Expand Down
25 changes: 23 additions & 2 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,37 @@ func ExampleLoad_nestedStruct() {
}

func ExampleLoad_nestedStructPrefixed() {
os.Setenv("DBHOST", "localhost")
os.Setenv("DBPORT", "5432")

var cfg struct {
DB struct {
Host string `env:"HOST"`
Port int `env:"PORT"`
} `env:"DB"`
}
if err := env.Load(&cfg, nil); err != nil {
fmt.Println(err)
}

fmt.Println(cfg.DB.Host)
fmt.Println(cfg.DB.Port)
// Output:
// localhost
// 5432
}

func ExampleLoad_nestedStructPrefixedWithSeparator() {
os.Setenv("DB_HOST", "localhost")
os.Setenv("DB_PORT", "5432")

var cfg struct {
DB struct {
Host string `env:"HOST"`
Port int `env:"PORT"`
} `env:"DB_"`
} `env:"DB"`
}
if err := env.Load(&cfg, nil); err != nil {
if err := env.Load(&cfg, &env.Options{NameSep: "_"}); err != nil {
fmt.Println(err)
}

Expand Down
2 changes: 1 addition & 1 deletion usage.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func Usage(cfg any, w io.Writer, opts *Options) {
v := pv.Elem()
vars, ok := cache[v.Type()]
if !ok {
vars = parseVars(v)
vars = parseVars(v, opts)
}

if u, ok := cfg.(interface {
Expand Down

0 comments on commit 8b5126e

Please sign in to comment.