From cb3bd9cb97270006ed83b98ebfb927dfd61aff55 Mon Sep 17 00:00:00 2001 From: Borja Lazaro Toralles Date: Fri, 15 Nov 2024 16:50:16 +0000 Subject: [PATCH 1/2] add-option-for-custom-environment --- envconfig.go | 57 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/envconfig.go b/envconfig.go index 7209d05..815592d 100644 --- a/envconfig.go +++ b/envconfig.go @@ -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 @@ -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 } @@ -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 { @@ -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 == "" { From 90d4b2c832ed19980bc7171d2a8c314ec2a01f66 Mon Sep 17 00:00:00 2001 From: Borja Lazaro Toralles Date: Fri, 15 Nov 2024 17:00:30 +0000 Subject: [PATCH 2/2] Add test --- envconfig_test.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/envconfig_test.go b/envconfig_test.go index 5b83321..2b7c13c 100644 --- a/envconfig_test.go +++ b/envconfig_test.go @@ -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) + } +}