diff --git a/README.md b/README.md index 8057af1d..eeb6350f 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Go Reference](https://pkg.go.dev/badge/github.com/obalunenko/getenv.svg)](https://pkg.go.dev/github.com/obalunenko/getenv) [![Go Report Card](https://goreportcard.com/badge/github.com/obalunenko/getenv)](https://goreportcard.com/report/github.com/obalunenko/getenv) [![codecov](https://codecov.io/gh/obalunenko/getenv/branch/master/graph/badge.svg)](https://codecov.io/gh/obalunenko/getenv) -![coverbadger-tag-do-not-edit](https://img.shields.io/badge/coverage-97.73%25-brightgreen?longCache=true&style=flat) +![coverbadger-tag-do-not-edit](https://img.shields.io/badge/coverage-97.58%25-brightgreen?longCache=true&style=flat) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=obalunenko_getenv&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=obalunenko_getenv) # getenv @@ -42,6 +42,7 @@ Types supported: - []time.Duration - bool - url.URL +- []url.URL - net.IP ## Examples diff --git a/getenv.go b/getenv.go index 6de977f8..5fe4a32b 100644 --- a/getenv.go +++ b/getenv.go @@ -33,6 +33,7 @@ // - []time.Duration // - bool // - url.URL +// - []url.URL // - net.IP package getenv diff --git a/getenv_test.go b/getenv_test.go index 95633eae..0cc78d93 100644 --- a/getenv_test.go +++ b/getenv_test.go @@ -3049,3 +3049,86 @@ func TestIPOrDefault(t *testing.T) { }) } } + +func TestURLSliceOrDefault(t *testing.T) { + type args struct { + key string + defaultVal []url.URL + separator string + } + + type expected struct { + val []url.URL + } + + var tests = []struct { + name string + precond precondition + args args + expected expected + }{ + { + name: "env not set - default returned", + precond: precondition{ + setenv: setenv{ + isSet: false, + val: "https://google.com,https://github.com", + }, + }, + args: args{ + key: testEnvKey, + defaultVal: []url.URL{getURL(t, "https://bing.com")}, + separator: ",", + }, + expected: expected{ + val: []url.URL{getURL(t, "https://bing.com")}, + }, + }, + { + name: "env set - env value returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "https://google.com,https://github.com", + }, + }, + args: args{ + key: testEnvKey, + defaultVal: []url.URL{getURL(t, "https://bing.com")}, + separator: ",", + }, + expected: expected{ + val: []url.URL{ + getURL(t, "https://google.com"), + getURL(t, "https://github.com"), + }, + }, + }, + { + name: "empty env value set - default returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "", + }, + }, + args: args{ + key: testEnvKey, + defaultVal: []url.URL{getURL(t, "https://bing.com")}, + separator: ",", + }, + expected: expected{ + val: []url.URL{getURL(t, "https://bing.com")}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.precond.maybeSetEnv(t, tt.args.key) + + got := getenv.EnvOrDefault(tt.args.key, tt.args.defaultVal, option.WithSeparator(",")) + assert.Equal(t, tt.expected.val, got) + }) + } +} diff --git a/internal/constraint.go b/internal/constraint.go index b6626e10..4a0e04cf 100644 --- a/internal/constraint.go +++ b/internal/constraint.go @@ -9,7 +9,7 @@ import ( type ( // EnvParsable is a constraint for supported environment variable types parsers. EnvParsable interface { - String | Int | Uint | Float | Time | bool | url.URL | net.IP + String | Int | Uint | Float | Time | bool | url.URL | []url.URL | net.IP } // String is a constraint for strings and slice of strings. diff --git a/internal/iface.go b/internal/iface.go index 21bfb22a..99ceb87b 100644 --- a/internal/iface.go +++ b/internal/iface.go @@ -27,6 +27,8 @@ func NewEnvParser(v any) EnvParser { p = newTimeParser(t) case url.URL: p = urlParser(t) + case []url.URL: + p = urlSliceParser(t) case net.IP: p = ipParser(t) default: @@ -429,6 +431,16 @@ func (t urlParser) ParseEnv(key string, defaltVal any, _ Parameters) any { return val } +type urlSliceParser []url.URL + +func (t urlSliceParser) ParseEnv(key string, defaltVal any, opts Parameters) any { + separator := opts.Separator + + val := urlSliceOrDefault(key, defaltVal.([]url.URL), separator) + + return val +} + type ipParser net.IP func (t ipParser) ParseEnv(key string, defaltVal any, _ Parameters) any { diff --git a/internal/iface_test.go b/internal/iface_test.go index 484c4d05..1aaef77d 100644 --- a/internal/iface_test.go +++ b/internal/iface_test.go @@ -280,6 +280,14 @@ func TestNewEnvParser(t *testing.T) { want: urlParser(url.URL{}), wantPanic: assert.NotPanics, }, + { + name: "[]url.URL", + args: args{ + v: []url.URL{}, + }, + want: urlSliceParser([]url.URL{}), + wantPanic: assert.NotPanics, + }, { name: "net.IP", args: args{ @@ -917,7 +925,7 @@ func Test_ParseEnv(t *testing.T) { precond: precondition{ setenv: setenv{ isSet: true, - val: "https.google.com", + val: "https://google.com", }, }, args: args{ @@ -928,7 +936,29 @@ func Test_ParseEnv(t *testing.T) { Layout: time.DateOnly, }, }, - want: getURL(t, "https.google.com"), + want: getURL(t, "https://google.com"), + }, + { + name: "urlSliceParser", + s: urlSliceParser([]url.URL{}), + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "https://google.com,https://bing.com", + }, + }, + args: args{ + key: testEnvKey, + defaltVal: []url.URL{}, + in2: Parameters{ + Separator: ",", + Layout: time.DateOnly, + }, + }, + want: []url.URL{ + getURL(t, "https://google.com"), + getURL(t, "https://bing.com"), + }, }, { name: "ipParser", diff --git a/internal/parsers.go b/internal/parsers.go index b67f201c..7e585076 100644 --- a/internal/parsers.go +++ b/internal/parsers.go @@ -739,6 +739,29 @@ func urlOrDefault(key string, defaultVal url.URL) url.URL { return *val } +// urlSliceOrDefault retrieves the url.URL slice value of the environment variable named +// by the key and separated by sep. +// If variable not set or value is empty - defaultVal will be returned. +func urlSliceOrDefault(key string, defaultVal []url.URL, sep string) []url.URL { + valraw := stringSliceOrDefault(key, nil, sep) + if valraw == nil { + return defaultVal + } + + val := make([]url.URL, 0, len(valraw)) + + for _, s := range valraw { + v, err := url.Parse(s) + if err != nil { + return defaultVal + } + + val = append(val, *v) + } + + return val +} + // ipOrDefault retrieves the net.IP value of the environment variable named // by the key represented by layout. // If variable not set or value is empty - defaultVal will be returned. diff --git a/internal/parsers_test.go b/internal/parsers_test.go index a35a7bab..948cdb11 100644 --- a/internal/parsers_test.go +++ b/internal/parsers_test.go @@ -3199,3 +3199,86 @@ func Test_ipOrDefault(t *testing.T) { }) } } + +func Test_urlSliceOrDefault(t *testing.T) { + type args struct { + key string + defaultVal []url.URL + separator string + } + + type expected struct { + val []url.URL + } + + var tests = []struct { + name string + precond precondition + args args + expected expected + }{ + { + name: "env not set - default returned", + precond: precondition{ + setenv: setenv{ + isSet: false, + val: "https://google.com,https://github.com", + }, + }, + args: args{ + key: testEnvKey, + defaultVal: []url.URL{getURL(t, "https://bing.com")}, + separator: ",", + }, + expected: expected{ + val: []url.URL{getURL(t, "https://bing.com")}, + }, + }, + { + name: "env set - env value returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "https://google.com,https://github.com", + }, + }, + args: args{ + key: testEnvKey, + defaultVal: []url.URL{getURL(t, "https://bing.com")}, + separator: ",", + }, + expected: expected{ + val: []url.URL{ + getURL(t, "https://google.com"), + getURL(t, "https://github.com"), + }, + }, + }, + { + name: "empty env value set - default returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "", + }, + }, + args: args{ + key: testEnvKey, + defaultVal: []url.URL{getURL(t, "https://bing.com")}, + separator: ",", + }, + expected: expected{ + val: []url.URL{getURL(t, "https://bing.com")}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.precond.maybeSetEnv(t, tt.args.key) + + got := urlSliceOrDefault(tt.args.key, tt.args.defaultVal, ",") + assert.Equal(t, tt.expected.val, got) + }) + } +}