Skip to content

Commit

Permalink
feat(sources/env): key prefix option, use settings struct to create s…
Browse files Browse the repository at this point in the history
…ource
  • Loading branch information
qdm12 committed Jan 14, 2024
1 parent 82456db commit c0eeccd
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 12 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Features:
- `OverrideWith`: `gosettings.OverrideWith*` functions (see [pkg.go.dev/github.com/qdm12/gosettings](https://pkg.go.dev/github.com/qdm12/gosettings))
- `Validate`: `validate.*` functions from [`github.com/qdm12/gosettings/validate`](https://pkg.go.dev/github.com/qdm12/gosettings/validate)
- Reading settings from multiple sources with precedence with [`github.com/qdm12/gosettings/reader`](https://pkg.go.dev/github.com/qdm12/gosettings/reader)
- Environment variable implementation `env.New(os.Environ())` in subpackage [`github.com/qdm12/gosettings/reader/sources/env`](https://pkg.go.dev/github.com/qdm12/gosettings/reader/sources/env)
- Environment variable implementation `env.New(env.Settings{Environ: os.Environ()})` in subpackage [`github.com/qdm12/gosettings/reader/sources/env`](https://pkg.go.dev/github.com/qdm12/gosettings/reader/sources/env)
- Flag implementation `flag.New(os.Args)` in subpackage [`github.com/qdm12/gosettings/reader/sources/flag`](https://pkg.go.dev/github.com/qdm12/gosettings/reader/sources/flag)
- Minor feature notes:
- No use of `reflect` for better runtime safety
Expand Down Expand Up @@ -111,7 +111,7 @@ A simple example (runnable [here](examples/reader/main.go)) would be:

```go
flagSource := flag.New([]string{"program", "--key1=A"})
envSource := env.New([]string{"KEY1=B", "KEY2=2"})
envSource := env.New(env.Settings{Environ: []string{"KEY1=B", "KEY2=2"}})
reader := reader.New(reader.Settings{
Sources: []reader.Source{flagSource, envSource},
})
Expand Down
2 changes: 1 addition & 1 deletion examples/reader/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (

func main() {
flagSource := flag.New([]string{"program", "--key1=A"})
envSource := env.New([]string{"KEY1=B", "KEY2=2"})
envSource := env.New(env.Settings{Environ: []string{"KEY1=B", "KEY2=2"}})
reader := reader.New(reader.Settings{
Sources: []reader.Source{flagSource, envSource},
})
Expand Down
4 changes: 2 additions & 2 deletions reader/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ type Settings struct {
// Sources is a slice of sources where a source at
// a lower index has a higher priority.
// It defaults to:
// []reader.Source{flag.New(os.Args), env.New(os.Environ())}
// []reader.Source{flag.New(os.Args), env.New(env.Settings{Environ: os.Environ()})}
Sources []Source
// HandleDeprecatedKey is called when encountering a deprecated
// key, and defaults to a no-op function.
Expand All @@ -54,7 +54,7 @@ type Settings struct {

func (s *Settings) setDefaults() {
s.Sources = gosettings.DefaultSlice(s.Sources,
[]Source{flag.New(os.Args), env.New(os.Environ())})
[]Source{flag.New(os.Args), env.New(env.Settings{Environ: os.Environ()})})

if s.HandleDeprecatedKey == nil { // Note: cannot use DefaultInterface
s.HandleDeprecatedKey = func(source, deprecatedKey, currentKey string) {}
Expand Down
11 changes: 8 additions & 3 deletions reader/sources/env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
// method.
type Source struct {
keyToValue map[string]string
keyPrefix string
}

// New creates a new environment variable source
Expand All @@ -17,12 +18,14 @@ type Source struct {
// Environ values with no '=' sign are ignored.
// All environment variable keys read are eventually
// transformed using the KeyTransform method.
func New(environ []string) (source *Source) {
func New(settings Settings) (source *Source) {
settings.setDefaults()
source = &Source{
keyToValue: make(map[string]string, len(environ)),
keyToValue: make(map[string]string, len(settings.Environ)),
keyPrefix: settings.KeyPrefix,
}

for _, keyValue := range environ {
for _, keyValue := range settings.Environ {
const maxParts = 2
parts := strings.SplitN(keyValue, "=", maxParts)
if len(parts) != maxParts {
Expand Down Expand Up @@ -53,8 +56,10 @@ func (s *Source) Get(key string) (value string, isSet bool) {
// variable key. It notably:
// - Changes all characters to be uppercase
// - Replaces all dashes with underscores.
// - Prefixes the key with the KeyPrefix field, without modifying the prefix.
func (s *Source) KeyTransform(key string) (newKey string) {
newKey = strings.ToUpper(key)
newKey = strings.ReplaceAll(newKey, "-", "_")
newKey = s.keyPrefix + newKey
return newKey
}
12 changes: 8 additions & 4 deletions reader/sources/env/env_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ func Test_New(t *testing.T) {
transformedLowercaseKey := strings.ToUpper(lowercaseKey)
testKeys[transformedLowercaseKey] = struct{}{}

env := New(os.Environ())
settings := Settings{
Environ: os.Environ(),
}

env := New(settings)

// Remove other test irrelevant environment variables
for k := range env.keyToValue {
Expand Down Expand Up @@ -70,16 +74,16 @@ func Test_Env_Get(t *testing.T) {
isSet bool
}{
"key_not_found": {
env: New([]string{}),
env: New(Settings{Environ: []string{}}),
key: "KEY",
},
"empty_value": {
env: New([]string{"KEY="}),
env: New(Settings{Environ: []string{"KEY="}}),
key: "KEY",
isSet: true,
},
"non_empty_value": {
env: New([]string{"KEY=value"}),
env: New(Settings{Environ: []string{"KEY=value"}}),
key: "KEY",
value: "value",
isSet: true,
Expand Down
27 changes: 27 additions & 0 deletions reader/sources/env/settings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package env

import (
"os"

"github.com/qdm12/gosettings"
)

// Settings contains settings for the environment variable source.
type Settings struct {
// Environ is a slice of "key=value" pairs which defaults
// to the returned value of os.Environ().
// Element without a '=' sign are ignored.
// All environment variable keys read are eventually
// transformed using the KeyTransform method.
// It defaults to the empty string.
Environ []string
// KeyPrefix is a prefix to add to all keys read from
// the environment. It is usually the program name to avoid
// conflict with other programs in the environment.
// It defaults to the empty string.
KeyPrefix string
}

func (s *Settings) setDefaults() {
s.Environ = gosettings.DefaultSlice(s.Environ, os.Environ())
}

0 comments on commit c0eeccd

Please sign in to comment.