diff --git a/README.md b/README.md index 9c2fab56..0b74eab7 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,12 @@ Types supported: - []url.URL - net.IP - []net.IP +- netip.Addr +- []netip.Addr +- netip.Prefix +- []netip.Prefix +- net.HardwareAddr +- []net.HardwareAddr - complex64 - []complex64 - complex128 @@ -190,11 +196,11 @@ Output: ``` [string]: golly; err: [int]: 123; err: -[int]: 0; err: could not parse variable[GH_GETENV_TEST] value[123s4] to type[int]: invalid value +[int]: 0; err: failed to parse environment variable[GH_GETENV_TEST]: strconv.ParseInt: parsing "123s4": invalid syntax: invalid value [time.Time]: 2022-01-20 00:00:00 +0000 UTC; err: [[]float64]: [26.89 0.67]; err: [time.Duration]: 2h35m0s; err: -[url.URL]: {https test:abcd123 golangbyexample.com:8000 /tutorials/intro false false type=advance&compact=false history }; err: +[url.URL]: https://test:abcd123@golangbyexample.com:8000/tutorials/intro?type=advance&compact=false#history; err: [net.IP]: 2001:cb8::17; err: [[]string]: [a b c d]; err: [complex128]: (1+2i); err: @@ -332,7 +338,7 @@ Output: [time.Time]: 2022-01-20 00:00:00 +0000 UTC [[]float64]: [26.89 0.67] [time.Duration]: 2h35m0s -[url.URL]: {https test:abcd123 golangbyexample.com:8000 /tutorials/intro false false type=advance&compact=false history } +[url.URL]: https://test:abcd123@golangbyexample.com:8000/tutorials/intro?type=advance&compact=false#history [net.IP]: 2001:cb8::17 [[]string]: [a b c d] [complex128]: (1+2i) diff --git a/getenv.go b/getenv.go index 9e3ac13c..a1d4919f 100644 --- a/getenv.go +++ b/getenv.go @@ -41,6 +41,12 @@ // - []url.URL // - net.IP // - []net.IP +// - netip.Addr +// - []netip.Addr +// - netip.Prefix +// - []netip.Prefix +// - net.HardwareAddr +// - []net.HardwareAddr // - complex64 // - []complex64 // - complex128 @@ -75,11 +81,17 @@ func Env[T internal.EnvParsable](key string, options ...option.Option) (T, error val, err := w.ParseEnv(key, params) if err != nil { if errors.Is(err, internal.ErrNotSet) { - return t, fmt.Errorf("failed to get environment variable[%s]: %w", key, ErrNotSet) + return t, fmt.Errorf("failed to get environment variable[%s]: %w", key, publicError{ + cause: err, + sentinel: ErrNotSet, + }) } if errors.Is(err, internal.ErrInvalidValue) { - return t, fmt.Errorf("failed to parse environment variable[%s]: %w", key, ErrInvalidValue) + return t, fmt.Errorf("failed to parse environment variable[%s]: %w", key, publicError{ + cause: err, + sentinel: ErrInvalidValue, + }) } return t, fmt.Errorf("failed to parse environment variable[%s]: %w", key, err) @@ -106,6 +118,20 @@ func EnvOrDefault[T internal.EnvParsable](key string, defaultVal T, options ...o return val } +// publicError keeps parser details while matching exported sentinels. +type publicError struct { + cause error + sentinel error +} + +func (e publicError) Error() string { + return e.cause.Error() +} + +func (e publicError) Unwrap() []error { + return []error{e.cause, e.sentinel} +} + // newParseParams creates new parameters from options. func newParseParams(opts []option.Option) internal.Parameters { var p internal.Parameters diff --git a/getenv_example_test.go b/getenv_example_test.go index 6a518c62..54d0b30a 100644 --- a/getenv_example_test.go +++ b/getenv_example_test.go @@ -241,7 +241,7 @@ func ExampleEnv() { // Output: // [string]: golly; err: // [int]: 123; err: - // [int]: 0; err: failed to parse environment variable[GH_GETENV_TEST]: invalid value + // [int]: 0; err: failed to parse environment variable[GH_GETENV_TEST]: strconv.ParseInt: parsing "123s4": invalid syntax: invalid value // [time.Time]: 2022-01-20 00:00:00 +0000 UTC; err: // [[]float64]: [26.89 0.67]; err: // [time.Duration]: 2h35m0s; err: diff --git a/getenv_test.go b/getenv_test.go index 21a1d65e..4e0af26a 100644 --- a/getenv_test.go +++ b/getenv_test.go @@ -3,6 +3,7 @@ package getenv_test import ( "errors" "net" + "net/netip" "net/url" "testing" "time" @@ -1393,7 +1394,7 @@ func TestTimeSliceOrDefault(t *testing.T) { 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.WithTimeLayout(tt.args.layout), option.WithSeparator(",")) + got := getenv.EnvOrDefault(tt.args.key, tt.args.defaultVal, option.WithTimeLayout(tt.args.layout), option.WithSeparator(tt.args.separator)) assert.Equal(t, tt.expected.val, got) }) } @@ -1475,7 +1476,7 @@ func TestDurationSliceOrDefault(t *testing.T) { 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(",")) + got := getenv.EnvOrDefault(tt.args.key, tt.args.defaultVal, option.WithSeparator(tt.args.separator)) assert.Equal(t, tt.expected.val, got) }) } @@ -2973,6 +2974,27 @@ func getIP(tb testing.TB, raw string) net.IP { return net.ParseIP(raw) } +func getNetIPAddr(tb testing.TB, raw string) netip.Addr { + tb.Helper() + + return netip.MustParseAddr(raw) +} + +func getNetIPPrefix(tb testing.TB, raw string) netip.Prefix { + tb.Helper() + + return netip.MustParsePrefix(raw) +} + +func getHardwareAddr(tb testing.TB, raw string) net.HardwareAddr { + tb.Helper() + + val, err := net.ParseMAC(raw) + require.NoError(tb, err) + + return val +} + func TestIPOrDefault(t *testing.T) { const rawDefault = "0.0.0.0" @@ -3136,6 +3158,495 @@ func TestIPSliceOrDefault(t *testing.T) { } } +func TestNetIPAddrOrDefault(t *testing.T) { + const rawDefault = "0.0.0.0" + + type args struct { + key string + defaultVal netip.Addr + } + + type expected struct { + val netip.Addr + } + + tests := []struct { + name string + precond precondition + args args + expected expected + }{ + { + name: "env not set - default returned", + precond: precondition{ + setenv: setenv{ + isSet: false, + val: "192.168.8.1", + }, + }, + args: args{ + key: testEnvKey, + defaultVal: getNetIPAddr(t, rawDefault), + }, + expected: expected{ + val: getNetIPAddr(t, rawDefault), + }, + }, + { + name: "env set - env value returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "192.168.8.1", + }, + }, + args: args{ + key: testEnvKey, + defaultVal: getNetIPAddr(t, rawDefault), + }, + expected: expected{ + val: getNetIPAddr(t, "192.168.8.1"), + }, + }, + { + name: "empty env value set - default returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "", + }, + }, + args: args{ + key: testEnvKey, + defaultVal: getNetIPAddr(t, rawDefault), + }, + expected: expected{ + val: getNetIPAddr(t, rawDefault), + }, + }, + } + + 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) + assert.Equal(t, tt.expected.val, got) + }) + } +} + +func TestNetIPAddrSliceOrDefault(t *testing.T) { + const rawDefault = "0.0.0.0" + + type args struct { + key string + defaultVal []netip.Addr + separator string + } + + type expected struct { + val []netip.Addr + } + + tests := []struct { + name string + precond precondition + args args + expected expected + }{ + { + name: "env not set - default returned", + precond: precondition{ + setenv: setenv{ + isSet: false, + val: "192.168.8.1,2001:db8::68", + }, + }, + args: args{ + key: testEnvKey, + defaultVal: []netip.Addr{getNetIPAddr(t, rawDefault)}, + separator: ",", + }, + expected: expected{ + val: []netip.Addr{getNetIPAddr(t, rawDefault)}, + }, + }, + { + name: "env set - env value returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "192.168.8.1,2001:db8::68", + }, + }, + args: args{ + key: testEnvKey, + defaultVal: []netip.Addr{getNetIPAddr(t, rawDefault)}, + separator: ",", + }, + expected: expected{ + val: []netip.Addr{ + getNetIPAddr(t, "192.168.8.1"), + getNetIPAddr(t, "2001:db8::68"), + }, + }, + }, + { + name: "empty env value set - default returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "", + }, + }, + args: args{ + key: testEnvKey, + defaultVal: []netip.Addr{getNetIPAddr(t, rawDefault)}, + separator: ",", + }, + expected: expected{ + val: []netip.Addr{getNetIPAddr(t, rawDefault)}, + }, + }, + } + + 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(tt.args.separator)) + assert.Equal(t, tt.expected.val, got) + }) + } +} + +func TestNetIPPrefixOrDefault(t *testing.T) { + const rawDefault = "0.0.0.0/0" + + type args struct { + key string + defaultVal netip.Prefix + } + + type expected struct { + val netip.Prefix + } + + tests := []struct { + name string + precond precondition + args args + expected expected + }{ + { + name: "env not set - default returned", + precond: precondition{ + setenv: setenv{ + isSet: false, + val: "192.168.8.0/24", + }, + }, + args: args{ + key: testEnvKey, + defaultVal: getNetIPPrefix(t, rawDefault), + }, + expected: expected{ + val: getNetIPPrefix(t, rawDefault), + }, + }, + { + name: "env set - env value returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "192.168.8.0/24", + }, + }, + args: args{ + key: testEnvKey, + defaultVal: getNetIPPrefix(t, rawDefault), + }, + expected: expected{ + val: getNetIPPrefix(t, "192.168.8.0/24"), + }, + }, + { + name: "empty env value set - default returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "", + }, + }, + args: args{ + key: testEnvKey, + defaultVal: getNetIPPrefix(t, rawDefault), + }, + expected: expected{ + val: getNetIPPrefix(t, rawDefault), + }, + }, + } + + 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) + assert.Equal(t, tt.expected.val, got) + }) + } +} + +func TestNetIPPrefixSliceOrDefault(t *testing.T) { + const rawDefault = "0.0.0.0/0" + + type args struct { + key string + defaultVal []netip.Prefix + separator string + } + + type expected struct { + val []netip.Prefix + } + + tests := []struct { + name string + precond precondition + args args + expected expected + }{ + { + name: "env not set - default returned", + precond: precondition{ + setenv: setenv{ + isSet: false, + val: "192.168.8.0/24,2001:db8::/64", + }, + }, + args: args{ + key: testEnvKey, + defaultVal: []netip.Prefix{getNetIPPrefix(t, rawDefault)}, + separator: ",", + }, + expected: expected{ + val: []netip.Prefix{getNetIPPrefix(t, rawDefault)}, + }, + }, + { + name: "env set - env value returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "192.168.8.0/24,2001:db8::/64", + }, + }, + args: args{ + key: testEnvKey, + defaultVal: []netip.Prefix{getNetIPPrefix(t, rawDefault)}, + separator: ",", + }, + expected: expected{ + val: []netip.Prefix{ + getNetIPPrefix(t, "192.168.8.0/24"), + getNetIPPrefix(t, "2001:db8::/64"), + }, + }, + }, + { + name: "empty env value set - default returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "", + }, + }, + args: args{ + key: testEnvKey, + defaultVal: []netip.Prefix{getNetIPPrefix(t, rawDefault)}, + separator: ",", + }, + expected: expected{ + val: []netip.Prefix{getNetIPPrefix(t, rawDefault)}, + }, + }, + } + + 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(tt.args.separator)) + assert.Equal(t, tt.expected.val, got) + }) + } +} + +func TestHardwareAddrOrDefault(t *testing.T) { + const rawDefault = "00:00:00:00:00:00" + + type args struct { + key string + defaultVal net.HardwareAddr + } + + type expected struct { + val net.HardwareAddr + } + + tests := []struct { + name string + precond precondition + args args + expected expected + }{ + { + name: "env not set - default returned", + precond: precondition{ + setenv: setenv{ + isSet: false, + val: "01:23:45:67:89:ab", + }, + }, + args: args{ + key: testEnvKey, + defaultVal: getHardwareAddr(t, rawDefault), + }, + expected: expected{ + val: getHardwareAddr(t, rawDefault), + }, + }, + { + name: "env set - env value returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "01:23:45:67:89:ab", + }, + }, + args: args{ + key: testEnvKey, + defaultVal: getHardwareAddr(t, rawDefault), + }, + expected: expected{ + val: getHardwareAddr(t, "01:23:45:67:89:ab"), + }, + }, + { + name: "empty env value set - default returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "", + }, + }, + args: args{ + key: testEnvKey, + defaultVal: getHardwareAddr(t, rawDefault), + }, + expected: expected{ + val: getHardwareAddr(t, rawDefault), + }, + }, + } + + 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) + assert.Equal(t, tt.expected.val, got) + }) + } +} + +func TestHardwareAddrSliceOrDefault(t *testing.T) { + const rawDefault = "00:00:00:00:00:00" + + type args struct { + key string + defaultVal []net.HardwareAddr + separator string + } + + type expected struct { + val []net.HardwareAddr + } + + tests := []struct { + name string + precond precondition + args args + expected expected + }{ + { + name: "env not set - default returned", + precond: precondition{ + setenv: setenv{ + isSet: false, + val: "01:23:45:67:89:ab,02:23:45:67:89:ab", + }, + }, + args: args{ + key: testEnvKey, + defaultVal: []net.HardwareAddr{getHardwareAddr(t, rawDefault)}, + separator: ",", + }, + expected: expected{ + val: []net.HardwareAddr{getHardwareAddr(t, rawDefault)}, + }, + }, + { + name: "env set - env value returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "01:23:45:67:89:ab,02:23:45:67:89:ab", + }, + }, + args: args{ + key: testEnvKey, + defaultVal: []net.HardwareAddr{getHardwareAddr(t, rawDefault)}, + separator: ",", + }, + expected: expected{ + val: []net.HardwareAddr{ + getHardwareAddr(t, "01:23:45:67:89:ab"), + getHardwareAddr(t, "02:23:45:67:89:ab"), + }, + }, + }, + { + name: "empty env value set - default returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "", + }, + }, + args: args{ + key: testEnvKey, + defaultVal: []net.HardwareAddr{getHardwareAddr(t, rawDefault)}, + separator: ",", + }, + expected: expected{ + val: []net.HardwareAddr{getHardwareAddr(t, rawDefault)}, + }, + }, + } + + 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(tt.args.separator)) + assert.Equal(t, tt.expected.val, got) + }) + } +} + func TestURLSliceOrDefault(t *testing.T) { type args struct { key string @@ -3213,7 +3724,7 @@ func TestURLSliceOrDefault(t *testing.T) { 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(",")) + got := getenv.EnvOrDefault(tt.args.key, tt.args.defaultVal, option.WithSeparator(tt.args.separator)) assert.Equal(t, tt.expected.val, got) }) } @@ -3915,6 +4426,7 @@ func TestEnvErrorSentinels(t *testing.T) { _, err := getenv.Env[int](testEnvKey) require.Error(t, err) assert.True(t, errors.Is(err, getenv.ErrInvalidValue)) + assert.ErrorContains(t, err, "strconv.ParseInt") _, err = getenv.Env[int]("GH_GETENV_TEST_NOT_SET") require.Error(t, err) @@ -3956,6 +4468,23 @@ func TestEnvIntSlice(t *testing.T) { wantError: errorEqual(getenv.ErrNotSet), }, }, + { + name: "env not set, no separator", + precond: precondition{ + setenv: setenv{ + isSet: false, + val: "1,2,3", + }, + }, + args: args{ + key: testEnvKey, + separator: "", + }, + expected: expected{ + val: nil, + wantError: errorEqual(getenv.ErrNotSet), + }, + }, { name: "env set", precond: precondition{ diff --git a/internal/common_test.go b/internal/common_test.go index 1465a821..b37c1f98 100644 --- a/internal/common_test.go +++ b/internal/common_test.go @@ -2,6 +2,7 @@ package internal import ( "net" + "net/netip" "net/url" "testing" @@ -50,6 +51,30 @@ func getTestIP(tb testing.TB, raw string) net.IP { return net.ParseIP(raw) } +// getTestNetIPAddr is a helper function for getting netip.Addr from string. +func getTestNetIPAddr(tb testing.TB, raw string) netip.Addr { + tb.Helper() + + return netip.MustParseAddr(raw) +} + +// getTestNetIPPrefix is a helper function for getting netip.Prefix from string. +func getTestNetIPPrefix(tb testing.TB, raw string) netip.Prefix { + tb.Helper() + + return netip.MustParsePrefix(raw) +} + +// getTestHardwareAddr is a helper function for getting net.HardwareAddr from string. +func getTestHardwareAddr(tb testing.TB, raw string) net.HardwareAddr { + tb.Helper() + + val, err := net.ParseMAC(raw) + require.NoError(tb, err) + + return val +} + func errorEqual(tb testing.TB, expected error) assert.ErrorAssertionFunc { tb.Helper() diff --git a/internal/constraint.go b/internal/constraint.go index 7e545fb5..1fb52f08 100644 --- a/internal/constraint.go +++ b/internal/constraint.go @@ -2,6 +2,7 @@ package internal import ( "net" + "net/netip" "net/url" "time" ) @@ -9,7 +10,7 @@ import ( type ( // EnvParsable is a constraint for types that can be parsed from environment variable. EnvParsable interface { - String | Number | NumberSlice | Time | Bool | URL | IP | Complex | ComplexSlice + String | Number | NumberSlice | Time | Bool | URL | Network | Complex | ComplexSlice } // String is a constraint for string and slice of strings. @@ -57,7 +58,7 @@ type ( []float32 | []float64 } - // Time is a constraint for time.Time and slice of time.Time. + // Time is a constraint for time.Time and time.Duration and slices of them. Time interface { time.Time | []time.Time | time.Duration | []time.Duration } @@ -72,9 +73,12 @@ type ( url.URL | []url.URL } - // IP is a constraint for net.IP and slice of net.IP. - IP interface { - net.IP | []net.IP + // Network is a constraint for network address types and slices of them. + Network interface { + net.IP | []net.IP | + net.HardwareAddr | []net.HardwareAddr | + netip.Addr | []netip.Addr | + netip.Prefix | []netip.Prefix } // ComplexSlice is a constraint for slice of complex. diff --git a/internal/iface.go b/internal/iface.go index c8fa9995..cead858d 100644 --- a/internal/iface.go +++ b/internal/iface.go @@ -4,6 +4,7 @@ package internal import ( "fmt" "net" + "net/netip" "net/url" "time" ) @@ -29,6 +30,10 @@ func NewEnvParser(v any) EnvParser { p = newURLParser(t) case net.IP, []net.IP: p = newIPParser(t) + case netip.Addr, []netip.Addr, netip.Prefix, []netip.Prefix: + p = newNetIPParser(t) + case net.HardwareAddr, []net.HardwareAddr: + p = newHardwareAddrParser(t) case complex64, []complex64, complex128, []complex128: p = newComplexParser(t) default: @@ -82,6 +87,34 @@ func newIPParser(v any) EnvParser { } } +// newNetIPParser is a constructor for net/netip parsers. +func newNetIPParser(v any) EnvParser { + switch t := v.(type) { + case netip.Addr: + return netIPAddrParser(t) + case []netip.Addr: + return netIPAddrSliceParser(t) + case netip.Prefix: + return netIPPrefixParser(t) + case []netip.Prefix: + return netIPPrefixSliceParser(t) + default: + return nil + } +} + +// newHardwareAddrParser is a constructor for net.HardwareAddr parsers. +func newHardwareAddrParser(v any) EnvParser { + switch t := v.(type) { + case net.HardwareAddr: + return hardwareAddrParser(t) + case []net.HardwareAddr: + return hardwareAddrSliceParser(t) + default: + return nil + } +} + func newStringParser(v any) EnvParser { switch t := v.(type) { case string: @@ -301,6 +334,54 @@ func (t ipSliceParser) ParseEnv(key string, opts Parameters) (any, error) { return getIPSlice(key, separator) } +// netIPAddrParser is a parser for netip.Addr. +type netIPAddrParser netip.Addr + +func (t netIPAddrParser) ParseEnv(key string, _ Parameters) (any, error) { + return getNetIPAddr(key) +} + +// netIPAddrSliceParser is a parser for []netip.Addr. +type netIPAddrSliceParser []netip.Addr + +func (t netIPAddrSliceParser) ParseEnv(key string, opts Parameters) (any, error) { + separator := opts.Separator + + return getNetIPAddrSlice(key, separator) +} + +// netIPPrefixParser is a parser for netip.Prefix. +type netIPPrefixParser netip.Prefix + +func (t netIPPrefixParser) ParseEnv(key string, _ Parameters) (any, error) { + return getNetIPPrefix(key) +} + +// netIPPrefixSliceParser is a parser for []netip.Prefix. +type netIPPrefixSliceParser []netip.Prefix + +func (t netIPPrefixSliceParser) ParseEnv(key string, opts Parameters) (any, error) { + separator := opts.Separator + + return getNetIPPrefixSlice(key, separator) +} + +// hardwareAddrParser is a parser for net.HardwareAddr. +type hardwareAddrParser net.HardwareAddr + +func (t hardwareAddrParser) ParseEnv(key string, _ Parameters) (any, error) { + return getHardwareAddr(key) +} + +// hardwareAddrSliceParser is a parser for []net.HardwareAddr. +type hardwareAddrSliceParser []net.HardwareAddr + +func (t hardwareAddrSliceParser) ParseEnv(key string, opts Parameters) (any, error) { + separator := opts.Separator + + return getHardwareAddrSlice(key, separator) +} + // boolSliceParser is a parser for []bool type boolSliceParser []bool diff --git a/internal/iface_test.go b/internal/iface_test.go index cfb5d4da..d28ddd21 100644 --- a/internal/iface_test.go +++ b/internal/iface_test.go @@ -3,6 +3,7 @@ package internal import ( "fmt" "net" + "net/netip" "net/url" "testing" "time" @@ -219,6 +220,36 @@ func TestNewEnvParser(t *testing.T) { wantPanic: assert.NotPanics, want: ipSliceParser([]net.IP{getTestIP(t, "0.0.0.0")}), }, + { + v: netip.MustParseAddr("127.0.0.1"), + wantPanic: assert.NotPanics, + want: netIPAddrParser(netip.MustParseAddr("127.0.0.1")), + }, + { + v: []netip.Addr{netip.MustParseAddr("127.0.0.1")}, + wantPanic: assert.NotPanics, + want: netIPAddrSliceParser([]netip.Addr{netip.MustParseAddr("127.0.0.1")}), + }, + { + v: netip.MustParsePrefix("192.168.0.0/24"), + wantPanic: assert.NotPanics, + want: netIPPrefixParser(netip.MustParsePrefix("192.168.0.0/24")), + }, + { + v: []netip.Prefix{netip.MustParsePrefix("192.168.0.0/24")}, + wantPanic: assert.NotPanics, + want: netIPPrefixSliceParser([]netip.Prefix{netip.MustParsePrefix("192.168.0.0/24")}), + }, + { + v: getTestHardwareAddr(t, "01:23:45:67:89:ab"), + wantPanic: assert.NotPanics, + want: hardwareAddrParser(getTestHardwareAddr(t, "01:23:45:67:89:ab")), + }, + { + v: []net.HardwareAddr{getTestHardwareAddr(t, "01:23:45:67:89:ab")}, + wantPanic: assert.NotPanics, + want: hardwareAddrSliceParser([]net.HardwareAddr{getTestHardwareAddr(t, "01:23:45:67:89:ab")}), + }, { v: uintptr(2), wantPanic: assert.NotPanics, @@ -480,6 +511,116 @@ func Test_newIPParser(t *testing.T) { } } +// Test_newNetIPParser tests newNetIPParser function. +func Test_newNetIPParser(t *testing.T) { + type args struct { + v any + } + + tests := []struct { + name string + args args + wantPanic panicAssertionFunc + want EnvParser + }{ + { + name: "netip.Addr", + args: args{ + v: netip.MustParseAddr("127.0.0.1"), + }, + wantPanic: assert.NotPanics, + want: netIPAddrParser(netip.MustParseAddr("127.0.0.1")), + }, + { + name: "netip.Addr slice", + args: args{ + v: []netip.Addr{netip.MustParseAddr("127.0.0.1")}, + }, + wantPanic: assert.NotPanics, + want: netIPAddrSliceParser([]netip.Addr{netip.MustParseAddr("127.0.0.1")}), + }, + { + name: "netip.Prefix", + args: args{ + v: netip.MustParsePrefix("192.168.0.0/24"), + }, + wantPanic: assert.NotPanics, + want: netIPPrefixParser(netip.MustParsePrefix("192.168.0.0/24")), + }, + { + name: "netip.Prefix slice", + args: args{ + v: []netip.Prefix{netip.MustParsePrefix("192.168.0.0/24")}, + }, + wantPanic: assert.NotPanics, + want: netIPPrefixSliceParser([]netip.Prefix{netip.MustParsePrefix("192.168.0.0/24")}), + }, + { + name: "not supported", + args: args{ + v: notsupported{}, + }, + wantPanic: assert.NotPanics, + want: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.wantPanic(t, func() { + assert.Equal(t, tt.want, newNetIPParser(tt.args.v)) + }) + }) + } +} + +// Test_newHardwareAddrParser tests newHardwareAddrParser function. +func Test_newHardwareAddrParser(t *testing.T) { + type args struct { + v any + } + + tests := []struct { + name string + args args + wantPanic panicAssertionFunc + want EnvParser + }{ + { + name: "net.HardwareAddr", + args: args{ + v: getTestHardwareAddr(t, "01:23:45:67:89:ab"), + }, + wantPanic: assert.NotPanics, + want: hardwareAddrParser(getTestHardwareAddr(t, "01:23:45:67:89:ab")), + }, + { + name: "net.HardwareAddr slice", + args: args{ + v: []net.HardwareAddr{getTestHardwareAddr(t, "01:23:45:67:89:ab")}, + }, + wantPanic: assert.NotPanics, + want: hardwareAddrSliceParser([]net.HardwareAddr{getTestHardwareAddr(t, "01:23:45:67:89:ab")}), + }, + { + name: "not supported", + args: args{ + v: notsupported{}, + }, + wantPanic: assert.NotPanics, + want: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.wantPanic(t, func() { + assert.Equal(t, tt.want, newHardwareAddrParser(tt.args.v)) + }) + }) + } +} + // Test_newURLParser tests newURLParser function. func Test_newURLParser(t *testing.T) { type args struct { diff --git a/internal/parsers.go b/internal/parsers.go index 0cf153ed..1b72cd5f 100644 --- a/internal/parsers.go +++ b/internal/parsers.go @@ -3,6 +3,7 @@ package internal import ( "fmt" "net" + "net/netip" "net/url" "os" "strconv" @@ -43,15 +44,15 @@ func getBool(key string) (bool, error) { } func getBoolSlice(key, sep string) ([]bool, error) { - if sep == "" { - return nil, ErrInvalidValue - } - env, err := getString(key) if err != nil { return nil, err } + if sep == "" { + return nil, ErrInvalidValue + } + val := strings.Split(env, sep) b := make([]bool, 0, len(val)) @@ -69,15 +70,15 @@ func getBoolSlice(key, sep string) ([]bool, error) { } func getStringSlice(key, sep string) ([]string, error) { - if sep == "" { - return nil, ErrInvalidValue - } - env, err := getString(key) if err != nil { return nil, err } + if sep == "" { + return nil, ErrInvalidValue + } + val := strings.Split(env, sep) return val, nil @@ -123,7 +124,7 @@ func parseSignedNumber[T Number](raw string, base, bits int) (T, error) { val, err := strconv.ParseInt(raw, base, bits) if err != nil { - return zero, ErrInvalidValue + return zero, newErrInvalidValue(err.Error()) } return T(val), nil @@ -134,7 +135,7 @@ func parseUnsignedNumber[T Number](raw string, base, bits int) (T, error) { val, err := strconv.ParseUint(raw, base, bits) if err != nil { - return zero, ErrInvalidValue + return zero, newErrInvalidValue(err.Error()) } return T(val), nil @@ -145,7 +146,7 @@ func parseFloatNumber[T Number](raw string, bits int) (T, error) { val, err := strconv.ParseFloat(raw, bits) if err != nil { - return zero, ErrInvalidValue + return zero, newErrInvalidValue(err.Error()) } return T(val), nil @@ -322,6 +323,108 @@ func getIPSlice(key, sep string) ([]net.IP, error) { return val, nil } +func getNetIPAddr(key string) (netip.Addr, error) { + env, err := getString(key) + if err != nil { + return netip.Addr{}, err + } + + val, err := netip.ParseAddr(env) + if err != nil { + return netip.Addr{}, newErrInvalidValue(err.Error()) + } + + return val, nil +} + +func getNetIPAddrSlice(key, sep string) ([]netip.Addr, error) { + env, err := getStringSlice(key, sep) + if err != nil { + return nil, err + } + + val := make([]netip.Addr, 0, len(env)) + + for _, s := range env { + v, err := netip.ParseAddr(s) + if err != nil { + return nil, newErrInvalidValue(err.Error()) + } + + val = append(val, v) + } + + return val, nil +} + +func getNetIPPrefix(key string) (netip.Prefix, error) { + env, err := getString(key) + if err != nil { + return netip.Prefix{}, err + } + + val, err := netip.ParsePrefix(env) + if err != nil { + return netip.Prefix{}, newErrInvalidValue(err.Error()) + } + + return val, nil +} + +func getNetIPPrefixSlice(key, sep string) ([]netip.Prefix, error) { + env, err := getStringSlice(key, sep) + if err != nil { + return nil, err + } + + val := make([]netip.Prefix, 0, len(env)) + + for _, s := range env { + v, err := netip.ParsePrefix(s) + if err != nil { + return nil, newErrInvalidValue(err.Error()) + } + + val = append(val, v) + } + + return val, nil +} + +func getHardwareAddr(key string) (net.HardwareAddr, error) { + env, err := getString(key) + if err != nil { + return nil, err + } + + val, err := net.ParseMAC(env) + if err != nil { + return nil, newErrInvalidValue(err.Error()) + } + + return val, nil +} + +func getHardwareAddrSlice(key, sep string) ([]net.HardwareAddr, error) { + env, err := getStringSlice(key, sep) + if err != nil { + return nil, err + } + + val := make([]net.HardwareAddr, 0, len(env)) + + for _, s := range env { + v, err := net.ParseMAC(s) + if err != nil { + return nil, newErrInvalidValue(err.Error()) + } + + val = append(val, v) + } + + return val, nil +} + func parseComplexGen[T Complex](raw string) (T, error) { var zero T diff --git a/internal/parsers_test.go b/internal/parsers_test.go index e9fe3ead..9abe5f73 100644 --- a/internal/parsers_test.go +++ b/internal/parsers_test.go @@ -2,6 +2,7 @@ package internal import ( "net" + "net/netip" "net/url" "testing" "time" @@ -791,6 +792,23 @@ func Test_getStringSlice(t *testing.T) { wantErr: assert.NoError, }, }, + { + name: "env not set, no separator - err returned", + precond: precondition{ + setenv: setenv{ + isSet: false, + val: "true,newval", + }, + }, + args: args{ + key: testEnvKey, + sep: "", + }, + expected: expected{ + val: nil, + wantErr: errorEqual(t, ErrNotSet), + }, + }, { name: "env set, no separator - err returned", precond: precondition{ @@ -821,7 +839,7 @@ func Test_getStringSlice(t *testing.T) { }, expected: expected{ val: nil, - wantErr: errorEqual(t, ErrInvalidValue), + wantErr: errorEqual(t, ErrNotSet), }, }, } @@ -938,7 +956,7 @@ func Test_getNumberSliceGenInt(t *testing.T) { }, expected: expected{ val: nil, - wantErr: errorEqual(t, ErrInvalidValue), + wantErr: errorEqual(t, ErrNotSet), }, }, } @@ -3526,14 +3544,110 @@ func Test_getIP(t *testing.T) { } } -func Test_getURLSlice(t *testing.T) { +func Test_getNetIPAddr(t *testing.T) { + type args struct { + key string + } + + type expected struct { + val netip.Addr + wantErr assert.ErrorAssertionFunc + } + + tests := []struct { + name string + precond precondition + args args + expected expected + }{ + { + name: "env not set - err returned", + precond: precondition{ + setenv: setenv{ + isSet: false, + val: "192.168.8.1", + }, + }, + args: args{ + key: testEnvKey, + }, + expected: expected{ + val: netip.Addr{}, + wantErr: errorEqual(t, ErrNotSet), + }, + }, + { + name: "env set - env value returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "192.168.8.1", + }, + }, + args: args{ + key: testEnvKey, + }, + expected: expected{ + val: getTestNetIPAddr(t, "192.168.8.1"), + wantErr: assert.NoError, + }, + }, + { + name: "env set, corrupted - err returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "192.168.8.1/24", + }, + }, + args: args{ + key: testEnvKey, + }, + expected: expected{ + val: netip.Addr{}, + wantErr: errorEqual(t, ErrInvalidValue), + }, + }, + { + name: "empty env value set - err returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "", + }, + }, + args: args{ + key: testEnvKey, + }, + expected: expected{ + val: netip.Addr{}, + wantErr: errorEqual(t, ErrNotSet), + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.precond.maybeSetEnv(t, tt.args.key) + + got, err := getNetIPAddr(tt.args.key) + if !tt.expected.wantErr(t, err) { + return + } + + assert.Equal(t, tt.expected.val, got) + }) + } +} + +func Test_getNetIPAddrSlice(t *testing.T) { type args struct { key string separator string } type expected struct { - val []url.URL + val []netip.Addr wantErr assert.ErrorAssertionFunc } @@ -3548,7 +3662,7 @@ func Test_getURLSlice(t *testing.T) { precond: precondition{ setenv: setenv{ isSet: false, - val: "https://google.com,https://github.com", + val: "192.168.8.1,2001:db8::68", }, }, args: args{ @@ -3565,7 +3679,7 @@ func Test_getURLSlice(t *testing.T) { precond: precondition{ setenv: setenv{ isSet: true, - val: "https://google.com,https://github.com", + val: "192.168.8.1,2001:db8::68", }, }, args: args{ @@ -3573,9 +3687,9 @@ func Test_getURLSlice(t *testing.T) { separator: ",", }, expected: expected{ - val: []url.URL{ - getTestURL(t, "https://google.com"), - getTestURL(t, "https://github.com"), + val: []netip.Addr{ + getTestNetIPAddr(t, "192.168.8.1"), + getTestNetIPAddr(t, "2001:db8::68"), }, wantErr: assert.NoError, }, @@ -3585,7 +3699,7 @@ func Test_getURLSlice(t *testing.T) { precond: precondition{ setenv: setenv{ isSet: true, - val: "https://google.com,htps://%%2github.com", + val: "192.168.8.1,sdsdsd", }, }, args: args{ @@ -3597,6 +3711,102 @@ func Test_getURLSlice(t *testing.T) { wantErr: errorEqual(t, ErrInvalidValue), }, }, + { + name: "env set, no separator - err returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "192.168.8.1,2001:db8::68", + }, + }, + args: args{ + key: testEnvKey, + }, + expected: expected{ + val: nil, + wantErr: errorEqual(t, ErrInvalidValue), + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.precond.maybeSetEnv(t, tt.args.key) + + got, err := getNetIPAddrSlice(tt.args.key, tt.args.separator) + if !tt.expected.wantErr(t, err) { + return + } + + assert.Equal(t, tt.expected.val, got) + }) + } +} + +func Test_getNetIPPrefix(t *testing.T) { + type args struct { + key string + } + + type expected struct { + val netip.Prefix + wantErr assert.ErrorAssertionFunc + } + + tests := []struct { + name string + precond precondition + args args + expected expected + }{ + { + name: "env not set - err returned", + precond: precondition{ + setenv: setenv{ + isSet: false, + val: "192.168.8.0/24", + }, + }, + args: args{ + key: testEnvKey, + }, + expected: expected{ + val: netip.Prefix{}, + wantErr: errorEqual(t, ErrNotSet), + }, + }, + { + name: "env set - env value returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "192.168.8.0/24", + }, + }, + args: args{ + key: testEnvKey, + }, + expected: expected{ + val: getTestNetIPPrefix(t, "192.168.8.0/24"), + wantErr: assert.NoError, + }, + }, + { + name: "env set, corrupted - err returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "192.168.8.1", + }, + }, + args: args{ + key: testEnvKey, + }, + expected: expected{ + val: netip.Prefix{}, + wantErr: errorEqual(t, ErrInvalidValue), + }, + }, { name: "empty env value set - err returned", precond: precondition{ @@ -3606,11 +3816,10 @@ func Test_getURLSlice(t *testing.T) { }, }, args: args{ - key: testEnvKey, - separator: ",", + key: testEnvKey, }, expected: expected{ - val: nil, + val: netip.Prefix{}, wantErr: errorEqual(t, ErrNotSet), }, }, @@ -3620,7 +3829,7 @@ func Test_getURLSlice(t *testing.T) { t.Run(tt.name, func(t *testing.T) { tt.precond.maybeSetEnv(t, tt.args.key) - got, err := getURLSlice(tt.args.key, ",") + got, err := getNetIPPrefix(tt.args.key) if !tt.expected.wantErr(t, err) { return } @@ -3630,14 +3839,14 @@ func Test_getURLSlice(t *testing.T) { } } -func Test_getIPSlice(t *testing.T) { +func Test_getNetIPPrefixSlice(t *testing.T) { type args struct { key string separator string } type expected struct { - val []net.IP + val []netip.Prefix wantErr assert.ErrorAssertionFunc } @@ -3652,7 +3861,7 @@ func Test_getIPSlice(t *testing.T) { precond: precondition{ setenv: setenv{ isSet: false, - val: "192.168.8.0,2001:cb8::17", + val: "192.168.8.0/24,2001:db8::/64", }, }, args: args{ @@ -3669,7 +3878,7 @@ func Test_getIPSlice(t *testing.T) { precond: precondition{ setenv: setenv{ isSet: true, - val: "192.168.8.0,2001:cb8::17", + val: "192.168.8.0/24,2001:db8::/64", }, }, args: args{ @@ -3677,9 +3886,9 @@ func Test_getIPSlice(t *testing.T) { separator: ",", }, expected: expected{ - val: []net.IP{ - getTestIP(t, "192.168.8.0"), - getTestIP(t, "2001:cb8::17"), + val: []netip.Prefix{ + getTestNetIPPrefix(t, "192.168.8.0/24"), + getTestNetIPPrefix(t, "2001:db8::/64"), }, wantErr: assert.NoError, }, @@ -3689,7 +3898,7 @@ func Test_getIPSlice(t *testing.T) { precond: precondition{ setenv: setenv{ isSet: true, - val: "192.168.8.0,sdsdsd", + val: "192.168.8.0/24,sdsdsd", }, }, args: args{ @@ -3702,20 +3911,19 @@ func Test_getIPSlice(t *testing.T) { }, }, { - name: "empty env value set - err returned", + name: "env set, no separator - err returned", precond: precondition{ setenv: setenv{ isSet: true, - val: "", + val: "192.168.8.0/24,2001:db8::/64", }, }, args: args{ - key: testEnvKey, - separator: ",", + key: testEnvKey, }, expected: expected{ val: nil, - wantErr: errorEqual(t, ErrNotSet), + wantErr: errorEqual(t, ErrInvalidValue), }, }, } @@ -3724,7 +3932,7 @@ func Test_getIPSlice(t *testing.T) { t.Run(tt.name, func(t *testing.T) { tt.precond.maybeSetEnv(t, tt.args.key) - got, err := getIPSlice(tt.args.key, tt.args.separator) + got, err := getNetIPPrefixSlice(tt.args.key, tt.args.separator) if !tt.expected.wantErr(t, err) { return } @@ -3734,15 +3942,13 @@ func Test_getIPSlice(t *testing.T) { } } -// Test_getBoolSlice tests the getBoolSlice function. -func Test_getBoolSlice(t *testing.T) { +func Test_getHardwareAddr(t *testing.T) { type args struct { - key string - separator string + key string } type expected struct { - val []bool + val net.HardwareAddr wantErr assert.ErrorAssertionFunc } @@ -3757,12 +3963,11 @@ func Test_getBoolSlice(t *testing.T) { precond: precondition{ setenv: setenv{ isSet: false, - val: "true,false", + val: "01:23:45:67:89:ab", }, }, args: args{ - key: testEnvKey, - separator: ",", + key: testEnvKey, }, expected: expected{ val: nil, @@ -3774,18 +3979,445 @@ func Test_getBoolSlice(t *testing.T) { precond: precondition{ setenv: setenv{ isSet: true, - val: "true,false", + val: "01:23:45:67:89:ab", }, }, args: args{ - key: testEnvKey, - separator: ",", + key: testEnvKey, }, expected: expected{ - val: []bool{true, false}, + val: getTestHardwareAddr(t, "01:23:45:67:89:ab"), wantErr: assert.NoError, }, }, + { + name: "env set, corrupted - err returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "not-a-mac", + }, + }, + args: args{ + key: testEnvKey, + }, + expected: expected{ + val: nil, + wantErr: errorEqual(t, ErrInvalidValue), + }, + }, + { + name: "empty env value set - err returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "", + }, + }, + args: args{ + key: testEnvKey, + }, + expected: expected{ + val: nil, + wantErr: errorEqual(t, ErrNotSet), + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.precond.maybeSetEnv(t, tt.args.key) + + got, err := getHardwareAddr(tt.args.key) + if !tt.expected.wantErr(t, err) { + return + } + + assert.Equal(t, tt.expected.val, got) + }) + } +} + +func Test_getHardwareAddrSlice(t *testing.T) { + type args struct { + key string + separator string + } + + type expected struct { + val []net.HardwareAddr + wantErr assert.ErrorAssertionFunc + } + + tests := []struct { + name string + precond precondition + args args + expected expected + }{ + { + name: "env not set - err returned", + precond: precondition{ + setenv: setenv{ + isSet: false, + val: "01:23:45:67:89:ab,02:23:45:67:89:ab", + }, + }, + args: args{ + key: testEnvKey, + separator: ",", + }, + expected: expected{ + val: nil, + wantErr: errorEqual(t, ErrNotSet), + }, + }, + { + name: "env set - env value returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "01:23:45:67:89:ab,02:23:45:67:89:ab", + }, + }, + args: args{ + key: testEnvKey, + separator: ",", + }, + expected: expected{ + val: []net.HardwareAddr{ + getTestHardwareAddr(t, "01:23:45:67:89:ab"), + getTestHardwareAddr(t, "02:23:45:67:89:ab"), + }, + wantErr: assert.NoError, + }, + }, + { + name: "env set, corrupted - err returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "01:23:45:67:89:ab,not-a-mac", + }, + }, + args: args{ + key: testEnvKey, + separator: ",", + }, + expected: expected{ + val: nil, + wantErr: errorEqual(t, ErrInvalidValue), + }, + }, + { + name: "env set, no separator - err returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "01:23:45:67:89:ab,02:23:45:67:89:ab", + }, + }, + args: args{ + key: testEnvKey, + }, + expected: expected{ + val: nil, + wantErr: errorEqual(t, ErrInvalidValue), + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.precond.maybeSetEnv(t, tt.args.key) + + got, err := getHardwareAddrSlice(tt.args.key, tt.args.separator) + if !tt.expected.wantErr(t, err) { + return + } + + assert.Equal(t, tt.expected.val, got) + }) + } +} + +func Test_getURLSlice(t *testing.T) { + type args struct { + key string + separator string + } + + type expected struct { + val []url.URL + wantErr assert.ErrorAssertionFunc + } + + tests := []struct { + name string + precond precondition + args args + expected expected + }{ + { + name: "env not set - err returned", + precond: precondition{ + setenv: setenv{ + isSet: false, + val: "https://google.com,https://github.com", + }, + }, + args: args{ + key: testEnvKey, + separator: ",", + }, + expected: expected{ + val: nil, + wantErr: errorEqual(t, ErrNotSet), + }, + }, + { + name: "env set - env value returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "https://google.com,https://github.com", + }, + }, + args: args{ + key: testEnvKey, + separator: ",", + }, + expected: expected{ + val: []url.URL{ + getTestURL(t, "https://google.com"), + getTestURL(t, "https://github.com"), + }, + wantErr: assert.NoError, + }, + }, + { + name: "env set, corrupted - err returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "https://google.com,htps://%%2github.com", + }, + }, + args: args{ + key: testEnvKey, + separator: ",", + }, + expected: expected{ + val: nil, + wantErr: errorEqual(t, ErrInvalidValue), + }, + }, + { + name: "empty env value set - err returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "", + }, + }, + args: args{ + key: testEnvKey, + separator: ",", + }, + expected: expected{ + val: nil, + wantErr: errorEqual(t, ErrNotSet), + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.precond.maybeSetEnv(t, tt.args.key) + + got, err := getURLSlice(tt.args.key, tt.args.separator) + if !tt.expected.wantErr(t, err) { + return + } + + assert.Equal(t, tt.expected.val, got) + }) + } +} + +func Test_getIPSlice(t *testing.T) { + type args struct { + key string + separator string + } + + type expected struct { + val []net.IP + wantErr assert.ErrorAssertionFunc + } + + tests := []struct { + name string + precond precondition + args args + expected expected + }{ + { + name: "env not set - err returned", + precond: precondition{ + setenv: setenv{ + isSet: false, + val: "192.168.8.0,2001:cb8::17", + }, + }, + args: args{ + key: testEnvKey, + separator: ",", + }, + expected: expected{ + val: nil, + wantErr: errorEqual(t, ErrNotSet), + }, + }, + { + name: "env set - env value returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "192.168.8.0,2001:cb8::17", + }, + }, + args: args{ + key: testEnvKey, + separator: ",", + }, + expected: expected{ + val: []net.IP{ + getTestIP(t, "192.168.8.0"), + getTestIP(t, "2001:cb8::17"), + }, + wantErr: assert.NoError, + }, + }, + { + name: "env set, corrupted - err returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "192.168.8.0,sdsdsd", + }, + }, + args: args{ + key: testEnvKey, + separator: ",", + }, + expected: expected{ + val: nil, + wantErr: errorEqual(t, ErrInvalidValue), + }, + }, + { + name: "empty env value set - err returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "", + }, + }, + args: args{ + key: testEnvKey, + separator: ",", + }, + expected: expected{ + val: nil, + wantErr: errorEqual(t, ErrNotSet), + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.precond.maybeSetEnv(t, tt.args.key) + + got, err := getIPSlice(tt.args.key, tt.args.separator) + if !tt.expected.wantErr(t, err) { + return + } + + assert.Equal(t, tt.expected.val, got) + }) + } +} + +// Test_getBoolSlice tests the getBoolSlice function. +func Test_getBoolSlice(t *testing.T) { + type args struct { + key string + separator string + } + + type expected struct { + val []bool + wantErr assert.ErrorAssertionFunc + } + + tests := []struct { + name string + precond precondition + args args + expected expected + }{ + { + name: "env not set - err returned", + precond: precondition{ + setenv: setenv{ + isSet: false, + val: "true,false", + }, + }, + args: args{ + key: testEnvKey, + separator: ",", + }, + expected: expected{ + val: nil, + wantErr: errorEqual(t, ErrNotSet), + }, + }, + { + name: "env set - env value returned", + precond: precondition{ + setenv: setenv{ + isSet: true, + val: "true,false", + }, + }, + args: args{ + key: testEnvKey, + separator: ",", + }, + expected: expected{ + val: []bool{true, false}, + wantErr: assert.NoError, + }, + }, + { + name: "env not set, no separator - err returned", + precond: precondition{ + setenv: setenv{ + isSet: false, + val: "true,false", + }, + }, + args: args{ + key: testEnvKey, + separator: "", + }, + expected: expected{ + val: nil, + wantErr: errorEqual(t, ErrNotSet), + }, + }, { name: "env set, corrupted - err returned", precond: precondition{ diff --git a/scripts/style/fmt.sh b/scripts/style/fmt.sh index e7a0488a..3a70ab47 100755 --- a/scripts/style/fmt.sh +++ b/scripts/style/fmt.sh @@ -21,7 +21,7 @@ echo "Local packages prefix: ${LOCAL_PFX}" for f in "${GO_FILES[@]}"; do echo "Fixing fmt at ${f}" - gofumpt -l -w "$f" + gofmt -l -w "$f" done echo "${SCRIPT_NAME} done."