Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 53 additions & 4 deletions envconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,9 @@ func gatherInfo(prefix string, spec interface{}) ([]varInfo, error) {
// CheckDisallowed checks that no environment variables with the prefix are set
// that we don't know how or want to parse. This is likely only meaningful with
// a non-empty prefix.
func CheckDisallowed(prefix string, spec interface{}) error {
func CheckDisallowed(prefix string, spec interface{}, options ...Option) error {
lc := defaultLibConfig(options...)

infos, err := gatherInfo(prefix, spec)
if err != nil {
return err
Expand All @@ -161,7 +163,7 @@ func CheckDisallowed(prefix string, spec interface{}) error {
prefix = strings.ToUpper(prefix) + "_"
}

for _, env := range os.Environ() {
for _, env := range lc.env.Environ() {
if !strings.HasPrefix(env, prefix) {
continue
}
Expand All @@ -174,8 +176,55 @@ func CheckDisallowed(prefix string, spec interface{}) error {
return nil
}

type (
Environment interface {
Getenv(key string) string
Environ() []string
}

libConfig struct {
env Environment
}

Option func(*libConfig)

defaultEnv struct{}
)

func (defaultEnv) Getenv(key string) string {
return os.Getenv(key)
}

func (defaultEnv) Environ() []string {
return os.Environ()
}

func WithEnvironment(env Environment) Option {
return func(lc *libConfig) {
lc.env = env
}
}

func defaultLibConfig(options ...Option) libConfig {
lc := libConfig{
env: defaultEnv{},
}

for _, option := range options {
option(&lc)
}

return lc
}

// Process populates the specified struct based on environment variables
func Process(prefix string, spec interface{}) error {
func Process(prefix string, spec interface{}, options ...Option) error {
lc := defaultLibConfig(options...)

for _, option := range options {
option(&lc)
}

infos, err := gatherInfo(prefix, spec)

for _, info := range infos {
Expand All @@ -184,7 +233,7 @@ func Process(prefix string, spec interface{}) error {
// we do not differentiate between explicitly set empty values, and
// values missing altogether. If a value is required, and it is empty,
// that is considered an error.
value := os.Getenv(info.Key)
value := lc.env.Getenv(info.Key)

def := info.Tags.Get("default")
if def != "" && value == "" {
Expand Down
30 changes: 30 additions & 0 deletions envconfig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1090,3 +1090,33 @@ func BenchmarkGatherInfo(b *testing.B) {
gatherInfo("env_config", &s)
}
}

type customEnvironment map[string]string

func (env customEnvironment) Getenv(key string) string {
return env[key]
}

func (env customEnvironment) Environ() []string {
keys := make([]string, 0, len(env))
for key := range env {
keys = append(keys, key)
}
return keys
}

func TestWithLookupEnv(t *testing.T) {
env := customEnvironment(map[string]string{
"ENV_PORT": "8080",
})
var cfg struct {
Port int `envconfig:"PORT" required:"true"`
}
err := Process("ENV", &cfg, WithEnvironment(env))
if err != nil {
t.Error(err.Error())
}
if cfg.Port != 8080 {
t.Errorf("expected %d, got %v", 1234, cfg.Port)
}
}