From 959dbf8e42b70a029464d546db647d10c97f3ee1 Mon Sep 17 00:00:00 2001 From: Tobias Salzmann <796084+Eun@users.noreply.github.com> Date: Wed, 19 Apr 2023 12:29:01 +0000 Subject: [PATCH 1/4] allow custom environment --- providers/env/env.go | 8 +++++++- providers/env/env_test.go | 3 +-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/providers/env/env.go b/providers/env/env.go index 5ec252bf..34e6f68c 100644 --- a/providers/env/env.go +++ b/providers/env/env.go @@ -15,6 +15,9 @@ type Env struct { prefix string delim string cb func(key string, value string) (string, interface{}) + // Environ can be used to specify a different set of enviornment variables. + // If empty Env will use os.Environ. + Environ []string } // Provider returns an environment variables provider that returns @@ -65,7 +68,10 @@ func (e *Env) ReadBytes() ([]byte, error) { func (e *Env) Read() (map[string]interface{}, error) { // Collect the environment variable keys. var keys []string - for _, k := range os.Environ() { + if len(e.Environ) == 0 { + e.Environ = os.Environ() + } + for _, k := range e.Environ { if e.prefix != "" { if strings.HasPrefix(k, e.prefix) { keys = append(keys, k) diff --git a/providers/env/env_test.go b/providers/env/env_test.go index 2717c711..afc53e0a 100644 --- a/providers/env/env_test.go +++ b/providers/env/env_test.go @@ -246,9 +246,8 @@ func TestRead(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - err := os.Setenv(tc.key, tc.value) + tc.Environ = []string{tc.key + "=" + tc.value} assert.Nil(t, err) - defer os.Unsetenv(tc.key) envs, err := tc.env.Read() assert.Nil(t, err) From 8f08c02d13922ab68c78d11a5219b171d436da7e Mon Sep 17 00:00:00 2001 From: Tobias Salzmann <796084+Eun@users.noreply.github.com> Date: Tue, 2 May 2023 15:11:27 +0200 Subject: [PATCH 2/4] add options --- providers/env/env.go | 54 +++++----- providers/env/env_test.go | 212 ++++++++++++++++++++++---------------- providers/env/options.go | 35 +++++++ 3 files changed, 191 insertions(+), 110 deletions(-) create mode 100644 providers/env/options.go diff --git a/providers/env/env.go b/providers/env/env.go index 34e6f68c..1058248d 100644 --- a/providers/env/env.go +++ b/providers/env/env.go @@ -12,12 +12,10 @@ import ( // Env implements an environment variables provider. type Env struct { - prefix string - delim string - cb func(key string, value string) (string, interface{}) - // Environ can be used to specify a different set of enviornment variables. - // If empty Env will use os.Environ. - Environ []string + prefix string + delim string + environ []string + cb func(key string, value string) (string, interface{}) } // Provider returns an environment variables provider that returns @@ -34,16 +32,13 @@ type Env struct { // If the callback returns an empty string, the variable will be // ignored. func Provider(prefix, delim string, cb func(s string) string) *Env { - e := &Env{ - prefix: prefix, - delim: delim, - } - if cb != nil { - e.cb = func(key string, value string) (string, interface{}) { + return ProviderWithOptions( + WithPrefix(prefix), + WithDelimiter(delim), + WithCallback(func(key string, value string) (string, interface{}) { return cb(key), value - } - } - return e + }), + ) } // ProviderWithValue works exactly the same as Provider except the callback @@ -51,11 +46,27 @@ func Provider(prefix, delim string, cb func(s string) string) *Env { // to modify both. This is useful for cases where you may want to return // other types like a string slice instead of just a string. func ProviderWithValue(prefix, delim string, cb func(key string, value string) (string, interface{})) *Env { - return &Env{ - prefix: prefix, - delim: delim, - cb: cb, + return ProviderWithOptions( + WithPrefix(prefix), + WithDelimiter(delim), + WithCallback(cb), + ) +} + +// ProviderWithOptions returns an environment variables provider that can be fine-tuned +// by using options. +func ProviderWithOptions(options ...Option) *Env { + e := &Env{ + prefix: "", + delim: ".", + cb: nil, + environ: os.Environ(), + } + + for _, option := range options { + option(e) } + return e } // ReadBytes is not supported by the env provider. @@ -68,10 +79,7 @@ func (e *Env) ReadBytes() ([]byte, error) { func (e *Env) Read() (map[string]interface{}, error) { // Collect the environment variable keys. var keys []string - if len(e.Environ) == 0 { - e.Environ = os.Environ() - } - for _, k := range e.Environ { + for _, k := range e.environ { if e.prefix != "" { if strings.HasPrefix(k, e.prefix) { keys = append(keys, k) diff --git a/providers/env/env_test.go b/providers/env/env_test.go index afc53e0a..7b187c9e 100644 --- a/providers/env/env_test.go +++ b/providers/env/env_test.go @@ -1,14 +1,14 @@ package env import ( - "github.com/stretchr/testify/assert" "os" "strings" "testing" + + "github.com/stretchr/testify/assert" ) func TestProvider(t *testing.T) { - testCases := []struct { name string prefix string @@ -18,17 +18,7 @@ func TestProvider(t *testing.T) { expKey string expValue string cb func(key string) string - want *Env }{ - { - name: "Nil cb", - prefix: "TESTVAR_", - delim: ".", - want: &Env{ - prefix: "TESTVAR_", - delim: ".", - }, - }, { name: "Simple cb", prefix: "TESTVAR_", @@ -40,19 +30,6 @@ func TestProvider(t *testing.T) { cb: func(key string) string { return strings.ToLower(key) }, - want: &Env{ - prefix: "TESTVAR_", - delim: ".", - }, - }, - { - name: "Empty string nil cb", - prefix: "", - delim: ".", - want: &Env{ - prefix: "", - delim: ".", - }, }, { name: "Cb is given", @@ -71,91 +48,151 @@ func TestProvider(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { gotProvider := Provider(tc.prefix, tc.delim, tc.cb) - if tc.cb == nil { - assert.Equal(t, tc.want, gotProvider) - } - if tc.cb != nil { - k, v := gotProvider.cb(tc.key, tc.value) - assert.Equal(t, tc.expKey, k) - assert.Equal(t, tc.expValue, v) - } + k, v := gotProvider.cb(tc.key, tc.value) + assert.Equal(t, tc.expKey, k) + assert.Equal(t, tc.expValue, v) }) } } func TestProviderWithValue(t *testing.T) { testCases := []struct { - name string - prefix string - delim string - cb func(key string, value string) (string, interface{}) - nilCallback bool - want *Env + name string + prefix string + delim string + cb func(key string, value string) (string, interface{}) + want *Env + }{ + { + name: "Custom cb function", + prefix: "TEST_", + delim: ".", + cb: func(key string, value string) (string, interface{}) { + key = strings.Replace(strings.TrimPrefix(strings.ToLower(key), "test_"), "_", ".", -1) + return key, value + }, + want: &Env{ + prefix: "TEST_", + delim: ".", + environ: os.Environ(), + cb: func(key string, value string) (string, interface{}) { + key = strings.Replace(strings.TrimPrefix(strings.ToLower(key), "test_"), "_", ".", -1) + return key, value + }, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + got := ProviderWithValue(tc.prefix, tc.delim, tc.cb) + keyGot, valGot := got.cb("test_key_env_1", "test_val") + keyWant, valWant := tc.want.cb("test_key_env_1", "test_val") + assert.Equal(t, tc.prefix, got.prefix) + assert.Equal(t, tc.delim, got.delim) + assert.Equal(t, keyWant, keyGot) + assert.Equal(t, valWant, valGot) + }) + } +} + +func TestProviderWithOptions(t *testing.T) { + testCases := []struct { + name string + options []Option + want *Env }{ { - name: "Nil cb", - prefix: "TEST_", - delim: ".", - nilCallback: true, + name: "Nil cb", + options: []Option{ + WithPrefix("TEST_"), + WithDelimiter("."), + WithEnviron([]string{"FOO=BAR"}), + WithCallback(nil), + }, want: &Env{ - prefix: "TEST_", - delim: ".", + prefix: "TEST_", + delim: ".", + environ: []string{"FOO=BAR"}, }, }, { - name: "Empty string nil cb", - prefix: "", - delim: ".", - nilCallback: true, + name: "Empty prefix nil cb", + options: []Option{ + WithPrefix(""), + WithDelimiter("."), + WithEnviron([]string{"FOO=BAR"}), + WithCallback(nil), + }, want: &Env{ - prefix: "", - delim: ".", + prefix: "", + delim: ".", + environ: []string{"FOO=BAR"}, }, }, { - name: "Return the same key-value pair in cb", - prefix: "TEST_", - delim: ".", - cb: func(key string, value string) (string, interface{}) { - return key, value + name: "Return the same key-value pair in cb", + options: []Option{ + WithPrefix("TEST_"), + WithDelimiter("."), + WithEnviron([]string{"FOO=BAR"}), + WithCallback(func(key string, value string) (string, interface{}) { + return key, value + }), }, want: &Env{ - prefix: "TEST_", - delim: ".", + prefix: "TEST_", + delim: ".", + environ: []string{"FOO=BAR"}, cb: func(key string, value string) (string, interface{}) { return key, value }, }, }, { - name: "Custom cb function", - prefix: "TEST_", - delim: ".", - cb: func(key string, value string) (string, interface{}) { - key = strings.Replace(strings.TrimPrefix(strings.ToLower(key), "test_"), "_", ".", -1) - return key, value + name: "Custom cb function", + options: []Option{ + WithPrefix("TEST_"), + WithDelimiter("."), + WithEnviron([]string{"FOO=BAR"}), + WithCallback(func(key string, value string) (string, interface{}) { + key = strings.Replace(strings.TrimPrefix(strings.ToLower(key), "test_"), "_", ".", -1) + return key, value + }), }, want: &Env{ - prefix: "TEST_", - delim: ".", + prefix: "TEST_", + delim: ".", + environ: []string{"FOO=BAR"}, cb: func(key string, value string) (string, interface{}) { key = strings.Replace(strings.TrimPrefix(strings.ToLower(key), "test_"), "_", ".", -1) return key, value }, }, }, + { + name: "with custom environment", + options: []Option{ + WithPrefix("TEST_"), + WithDelimiter("."), + WithEnviron([]string{"FOO=BAR"}), + }, + want: &Env{ + prefix: "TEST_", + delim: ".", + environ: []string{"FOO=BAR"}, + }, + }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - got := ProviderWithValue(tc.prefix, tc.delim, tc.cb) - if tc.nilCallback { + got := ProviderWithOptions(tc.options...) + if got.cb == nil { assert.Equal(t, tc.want, got) } else { keyGot, valGot := got.cb("test_key_env_1", "test_val") keyWant, valWant := tc.want.cb("test_key_env_1", "test_val") - assert.Equal(t, tc.prefix, got.prefix) - assert.Equal(t, tc.delim, got.delim) assert.Equal(t, keyWant, keyGot) assert.Equal(t, valWant, valGot) } @@ -166,30 +203,30 @@ func TestProviderWithValue(t *testing.T) { func TestRead(t *testing.T) { testCases := []struct { name string - key string - value string expKey string expValue string env *Env }{ { name: "No cb", - key: "TEST_KEY", - value: "TEST_VAL", expKey: "TEST_KEY", expValue: "TEST_VAL", env: &Env{ delim: ".", + environ: []string{ + "TEST_KEY=TEST_VAL", + }, }, }, { name: "cb given", - key: "TEST_KEY", - value: "TEST_VAL", expKey: "test.key", expValue: "TEST_VAL", env: &Env{ delim: "_", + environ: []string{ + "TEST_KEY=TEST_VAL", + }, cb: func(key string, value string) (string, interface{}) { return strings.Replace(strings.ToLower(key), "_", ".", -1), value }, @@ -197,13 +234,14 @@ func TestRead(t *testing.T) { }, { name: "No cb - prefix given", - key: "TEST_KEY", - value: "TEST_VAL", expKey: "test.key", expValue: "TEST_VAL", env: &Env{ prefix: "TEST", delim: "/", + environ: []string{ + "TEST_KEY=TEST_VAL", + }, cb: func(key string, value string) (string, interface{}) { return strings.Replace(strings.ToLower(key), "_", ".", -1), value }, @@ -211,22 +249,24 @@ func TestRead(t *testing.T) { }, { name: "Path value", - key: "TEST_DIR", - value: "/test/dir/file", expKey: "TEST_DIR", expValue: "/test/dir/file", env: &Env{ + environ: []string{ + "TEST_DIR=/test/dir/file", + }, delim: ".", }, }, { name: "Replace value with underscore", - key: "TEST_DIR", - value: "/test/dir/file", expKey: "TEST_DIR", expValue: "_test_dir_file", env: &Env{ delim: ".", + environ: []string{ + "TEST_DIR=/test/dir/file", + }, cb: func(key string, value string) (string, interface{}) { return key, strings.Replace(strings.ToLower(value), "/", "_", -1) }, @@ -234,11 +274,12 @@ func TestRead(t *testing.T) { }, { name: "Empty value", - key: "KEY", - value: "", expKey: "KEY", expValue: "", env: &Env{ + environ: []string{ + "KEY=", + }, delim: ".", }, }, @@ -246,9 +287,6 @@ func TestRead(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - tc.Environ = []string{tc.key + "=" + tc.value} - assert.Nil(t, err) - envs, err := tc.env.Read() assert.Nil(t, err) v, ok := envs[tc.expKey] diff --git a/providers/env/options.go b/providers/env/options.go new file mode 100644 index 00000000..98977aff --- /dev/null +++ b/providers/env/options.go @@ -0,0 +1,35 @@ +package env + +type Option func(*Env) + +// WithPrefix sets the environment prefix. Only the env vars with the prefix are captured. +func WithPrefix(prefix string) Option { + return func(env *Env) { + env.prefix = prefix + } +} + +// WithDelimiter sets the delimiter to split the environment variable into its parts. +// For example the delimiter "." will convert the key `parent.child.key: 1` +// to `{parent: {child: {key: 1}}}`. +func WithDelimiter(delim string) Option { + return func(env *Env) { + env.delim = delim + } +} + +// WithCallback sets the function that will be called for each environment variable. +// This is useful for cases where you may want to modify the variable or value before it gets passed on. +// If the callback returns an empty string, the variable will be +// ignored. +func WithCallback(cb func(key string, value string) (string, interface{})) Option { + return func(env *Env) { + env.cb = cb + } +} + +func WithEnviron(environ []string) Option { + return func(env *Env) { + env.environ = environ + } +} From 35c9ffde3cb2cb5562a2c745d159fa4957cb21a7 Mon Sep 17 00:00:00 2001 From: Tobias Salzmann <796084+Eun@users.noreply.github.com> Date: Tue, 2 May 2023 15:58:43 +0200 Subject: [PATCH 3/4] add WithEnvironMap --- providers/env/env_test.go | 17 ++++++++++++++++- providers/env/options.go | 11 +++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/providers/env/env_test.go b/providers/env/env_test.go index 7b187c9e..548593ef 100644 --- a/providers/env/env_test.go +++ b/providers/env/env_test.go @@ -171,7 +171,7 @@ func TestProviderWithOptions(t *testing.T) { }, }, { - name: "with custom environment", + name: "with custom environment slice", options: []Option{ WithPrefix("TEST_"), WithDelimiter("."), @@ -183,6 +183,21 @@ func TestProviderWithOptions(t *testing.T) { environ: []string{"FOO=BAR"}, }, }, + { + name: "with custom environment map", + options: []Option{ + WithPrefix("TEST_"), + WithDelimiter("."), + WithEnvironMap(map[string]string{ + "FOO": "BAR", + }), + }, + want: &Env{ + prefix: "TEST_", + delim: ".", + environ: []string{"FOO=BAR"}, + }, + }, } for _, tc := range testCases { diff --git a/providers/env/options.go b/providers/env/options.go index 98977aff..cc99780f 100644 --- a/providers/env/options.go +++ b/providers/env/options.go @@ -28,8 +28,19 @@ func WithCallback(cb func(key string, value string) (string, interface{})) Optio } } +// WithEnviron sets the environment using a traditional environment slice. func WithEnviron(environ []string) Option { return func(env *Env) { env.environ = environ } } + +// WithEnvironMap sets the environment using a map. +func WithEnvironMap(environ map[string]string) Option { + return func(env *Env) { + env.environ = make([]string, 0, len(environ)) + for k, v := range environ { + env.environ = append(env.environ, k+"="+v) + } + } +} From 4631660b11fc3a61603951af2fd25bbe768af142 Mon Sep 17 00:00:00 2001 From: Tobias Salzmann <796084+Eun@users.noreply.github.com> Date: Mon, 19 Jun 2023 10:48:53 +0200 Subject: [PATCH 4/4] use func for getting the environment --- providers/env/env.go | 20 +++++---- providers/env/env_test.go | 95 +++++++++++++++++++++++---------------- providers/env/options.go | 22 ++++++--- 3 files changed, 83 insertions(+), 54 deletions(-) diff --git a/providers/env/env.go b/providers/env/env.go index 1058248d..602c43bb 100644 --- a/providers/env/env.go +++ b/providers/env/env.go @@ -12,10 +12,10 @@ import ( // Env implements an environment variables provider. type Env struct { - prefix string - delim string - environ []string - cb func(key string, value string) (string, interface{}) + prefix string + delim string + environFunc func() []string + cb func(key string, value string) (string, interface{}) } // Provider returns an environment variables provider that returns @@ -57,10 +57,12 @@ func ProviderWithValue(prefix, delim string, cb func(key string, value string) ( // by using options. func ProviderWithOptions(options ...Option) *Env { e := &Env{ - prefix: "", - delim: ".", - cb: nil, - environ: os.Environ(), + prefix: "", + delim: ".", + cb: nil, + environFunc: func() []string { + return os.Environ() + }, } for _, option := range options { @@ -79,7 +81,7 @@ func (e *Env) ReadBytes() ([]byte, error) { func (e *Env) Read() (map[string]interface{}, error) { // Collect the environment variable keys. var keys []string - for _, k := range e.environ { + for _, k := range e.environFunc() { if e.prefix != "" { if strings.HasPrefix(k, e.prefix) { keys = append(keys, k) diff --git a/providers/env/env_test.go b/providers/env/env_test.go index 548593ef..ee4f2f77 100644 --- a/providers/env/env_test.go +++ b/providers/env/env_test.go @@ -72,9 +72,11 @@ func TestProviderWithValue(t *testing.T) { return key, value }, want: &Env{ - prefix: "TEST_", - delim: ".", - environ: os.Environ(), + prefix: "TEST_", + delim: ".", + environFunc: func() []string { + return os.Environ() + }, cb: func(key string, value string) (string, interface{}) { key = strings.Replace(strings.TrimPrefix(strings.ToLower(key), "test_"), "_", ".", -1) return key, value @@ -111,9 +113,11 @@ func TestProviderWithOptions(t *testing.T) { WithCallback(nil), }, want: &Env{ - prefix: "TEST_", - delim: ".", - environ: []string{"FOO=BAR"}, + prefix: "TEST_", + delim: ".", + environFunc: func() []string { + return []string{"FOO=BAR"} + }, }, }, { @@ -125,9 +129,11 @@ func TestProviderWithOptions(t *testing.T) { WithCallback(nil), }, want: &Env{ - prefix: "", - delim: ".", - environ: []string{"FOO=BAR"}, + prefix: "", + delim: ".", + environFunc: func() []string { + return []string{"FOO=BAR"} + }, }, }, { @@ -141,9 +147,11 @@ func TestProviderWithOptions(t *testing.T) { }), }, want: &Env{ - prefix: "TEST_", - delim: ".", - environ: []string{"FOO=BAR"}, + prefix: "TEST_", + delim: ".", + environFunc: func() []string { + return []string{"FOO=BAR"} + }, cb: func(key string, value string) (string, interface{}) { return key, value }, @@ -161,9 +169,11 @@ func TestProviderWithOptions(t *testing.T) { }), }, want: &Env{ - prefix: "TEST_", - delim: ".", - environ: []string{"FOO=BAR"}, + prefix: "TEST_", + delim: ".", + environFunc: func() []string { + return []string{"FOO=BAR"} + }, cb: func(key string, value string) (string, interface{}) { key = strings.Replace(strings.TrimPrefix(strings.ToLower(key), "test_"), "_", ".", -1) return key, value @@ -178,9 +188,11 @@ func TestProviderWithOptions(t *testing.T) { WithEnviron([]string{"FOO=BAR"}), }, want: &Env{ - prefix: "TEST_", - delim: ".", - environ: []string{"FOO=BAR"}, + prefix: "TEST_", + delim: ".", + environFunc: func() []string { + return []string{"FOO=BAR"} + }, }, }, { @@ -193,9 +205,11 @@ func TestProviderWithOptions(t *testing.T) { }), }, want: &Env{ - prefix: "TEST_", - delim: ".", - environ: []string{"FOO=BAR"}, + prefix: "TEST_", + delim: ".", + environFunc: func() []string { + return []string{"FOO=BAR"} + }, }, }, } @@ -203,13 +217,18 @@ func TestProviderWithOptions(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { got := ProviderWithOptions(tc.options...) - if got.cb == nil { + if got.cb == nil && got.environFunc == nil { assert.Equal(t, tc.want, got) } else { - keyGot, valGot := got.cb("test_key_env_1", "test_val") - keyWant, valWant := tc.want.cb("test_key_env_1", "test_val") - assert.Equal(t, keyWant, keyGot) - assert.Equal(t, valWant, valGot) + if got.cb != nil { + keyGot, valGot := got.cb("test_key_env_1", "test_val") + keyWant, valWant := tc.want.cb("test_key_env_1", "test_val") + assert.Equal(t, keyWant, keyGot) + assert.Equal(t, valWant, valGot) + } + if got.environFunc != nil { + assert.Equal(t, tc.want.environFunc(), got.environFunc()) + } } }) } @@ -228,8 +247,8 @@ func TestRead(t *testing.T) { expValue: "TEST_VAL", env: &Env{ delim: ".", - environ: []string{ - "TEST_KEY=TEST_VAL", + environFunc: func() []string { + return []string{"TEST_KEY=TEST_VAL"} }, }, }, @@ -239,8 +258,8 @@ func TestRead(t *testing.T) { expValue: "TEST_VAL", env: &Env{ delim: "_", - environ: []string{ - "TEST_KEY=TEST_VAL", + environFunc: func() []string { + return []string{"TEST_KEY=TEST_VAL"} }, cb: func(key string, value string) (string, interface{}) { return strings.Replace(strings.ToLower(key), "_", ".", -1), value @@ -254,8 +273,8 @@ func TestRead(t *testing.T) { env: &Env{ prefix: "TEST", delim: "/", - environ: []string{ - "TEST_KEY=TEST_VAL", + environFunc: func() []string { + return []string{"TEST_KEY=TEST_VAL"} }, cb: func(key string, value string) (string, interface{}) { return strings.Replace(strings.ToLower(key), "_", ".", -1), value @@ -267,8 +286,8 @@ func TestRead(t *testing.T) { expKey: "TEST_DIR", expValue: "/test/dir/file", env: &Env{ - environ: []string{ - "TEST_DIR=/test/dir/file", + environFunc: func() []string { + return []string{"TEST_DIR=/test/dir/file"} }, delim: ".", }, @@ -279,8 +298,8 @@ func TestRead(t *testing.T) { expValue: "_test_dir_file", env: &Env{ delim: ".", - environ: []string{ - "TEST_DIR=/test/dir/file", + environFunc: func() []string { + return []string{"TEST_DIR=/test/dir/file"} }, cb: func(key string, value string) (string, interface{}) { return key, strings.Replace(strings.ToLower(value), "/", "_", -1) @@ -292,8 +311,8 @@ func TestRead(t *testing.T) { expKey: "KEY", expValue: "", env: &Env{ - environ: []string{ - "KEY=", + environFunc: func() []string { + return []string{"KEY="} }, delim: ".", }, diff --git a/providers/env/options.go b/providers/env/options.go index cc99780f..cd9ea56f 100644 --- a/providers/env/options.go +++ b/providers/env/options.go @@ -28,19 +28,27 @@ func WithCallback(cb func(key string, value string) (string, interface{})) Optio } } -// WithEnviron sets the environment using a traditional environment slice. -func WithEnviron(environ []string) Option { +// WithEnvironFunc sets the environment using a function +func WithEnvironFunc(environFunc func() []string) Option { return func(env *Env) { - env.environ = environ + env.environFunc = environFunc } } +// WithEnviron sets the environment using a traditional environment slice. +func WithEnviron(environ []string) Option { + return WithEnvironFunc(func() []string { + return environ + }) +} + // WithEnvironMap sets the environment using a map. func WithEnvironMap(environ map[string]string) Option { - return func(env *Env) { - env.environ = make([]string, 0, len(environ)) + return WithEnvironFunc(func() []string { + s := make([]string, 0, len(environ)) for k, v := range environ { - env.environ = append(env.environ, k+"="+v) + s = append(s, k+"="+v) } - } + return s + }) }