From 505b070278fa0f72a6e413da6cc47a7d4e93eac4 Mon Sep 17 00:00:00 2001 From: Oleg Balunenko Date: Wed, 26 Jul 2023 19:24:28 +0400 Subject: [PATCH] refactor: Implement slices generic parsers (#109) * refactor: Implement int/float slice generic parsers * refactor: Implement complex slice generic parser * refactor: Implement uint slice generic parser * style: Fix linter warnings * style: Fix linter warnings * refactor: Reduce cognitive complexity * refactor: Use generic slices parsers * refactor: Use generic uint slice parcer * refactor: Use golang.org/x/exp/constraints * refactor: Simplify code * refactor: Simplify code --- go.mod | 5 +- go.sum | 2 + internal/constraint.go | 36 +- internal/errors.go | 10 + internal/iface.go | 30 +- internal/parsers.go | 534 ++++-------------- internal/parsers_test.go | 34 +- vendor/golang.org/x/exp/LICENSE | 27 + vendor/golang.org/x/exp/PATENTS | 22 + .../x/exp/constraints/constraints.go | 50 ++ vendor/modules.txt | 3 + 11 files changed, 272 insertions(+), 481 deletions(-) create mode 100644 internal/errors.go create mode 100644 vendor/golang.org/x/exp/LICENSE create mode 100644 vendor/golang.org/x/exp/PATENTS create mode 100644 vendor/golang.org/x/exp/constraints/constraints.go diff --git a/go.mod b/go.mod index 2567f18c..7d64db49 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,10 @@ module github.com/obalunenko/getenv go 1.20 -require github.com/stretchr/testify v1.8.4 +require ( + github.com/stretchr/testify v1.8.4 + golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 +) require ( github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/go.sum b/go.sum index fa4b6e68..3d595ec4 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY= +golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/internal/constraint.go b/internal/constraint.go index cdc0724d..267b5c67 100644 --- a/internal/constraint.go +++ b/internal/constraint.go @@ -4,12 +4,14 @@ import ( "net" "net/url" "time" + + "golang.org/x/exp/constraints" ) type ( // EnvParsable is a constraint for types that can be parsed from environment variable. EnvParsable interface { - String | Int | Uint | Float | Time | Bool | URL | IP | Complex + String | Int | IntSlice | Uint | UintSlice | Float | FloatSlice | Time | Bool | URL | IP | Complex | ComplexSlice } // String is a constraint for string and slice of strings. @@ -18,19 +20,28 @@ type ( } // Int is a constraint for integer and slice of integers. - Int interface { - int | []int | int8 | []int8 | int16 | []int16 | int32 | []int32 | int64 | []int64 + Int = constraints.Signed + + // IntSlice is a constraint for slice of integers. + IntSlice interface { + []int | []int8 | []int16 | []int32 | []int64 + } + + // UintSlice is a constraint for slice of unsigned integers. + UintSlice interface { + []uint | []uint8 | []uint16 | []uint32 | []uint64 | []uintptr } // Uint is a constraint for unsigned integer and slice of unsigned integers. - Uint interface { - uint | []uint | uint8 | []uint8 | uint16 | []uint16 | uint32 | []uint32 | uint64 | []uint64 | uintptr | []uintptr + Uint = constraints.Unsigned + + // FloatSlice is a constraint for slice of floats. + FloatSlice interface { + []float32 | []float64 } // Float is a constraint for float and slice of floats. - Float interface { - float32 | []float32 | float64 | []float64 - } + Float = constraints.Float // Time is a constraint for time.Time and slice of time.Time. Time interface { @@ -52,8 +63,11 @@ type ( net.IP | []net.IP } - // Complex is a constraint for complex and slice of complex. - Complex interface { - complex64 | []complex64 | complex128 | []complex128 + // ComplexSlice is a constraint for slice of complex. + ComplexSlice interface { + []complex64 | []complex128 } + + // Complex is a constraint for complex and slice of complex. + Complex = constraints.Complex ) diff --git a/internal/errors.go b/internal/errors.go new file mode 100644 index 00000000..38c65318 --- /dev/null +++ b/internal/errors.go @@ -0,0 +1,10 @@ +package internal + +import "errors" + +var ( + // ErrNotSet is an error that is returned when the environment variable is not set. + ErrNotSet = errors.New("not set") + // ErrInvalidValue is an error that is returned when the environment variable is not valid. + ErrInvalidValue = errors.New("invalid value") +) diff --git a/internal/iface.go b/internal/iface.go index 0845ea47..bd9cc685 100644 --- a/internal/iface.go +++ b/internal/iface.go @@ -235,7 +235,7 @@ type intSliceParser []int func (i intSliceParser) ParseEnv(key string, defaltVal any, options Parameters) any { sep := options.Separator - val := intSliceOrDefault(key, defaltVal.([]int), sep) + val := intSliceOrDefaultGen(key, defaltVal.([]int), sep) return val } @@ -245,7 +245,7 @@ type float32SliceParser []float32 func (i float32SliceParser) ParseEnv(key string, defaltVal any, options Parameters) any { sep := options.Separator - val := float32SliceOrDefault(key, defaltVal.([]float32), sep) + val := floatSliceOrDefaultGen(key, defaltVal.([]float32), sep) return val } @@ -255,7 +255,7 @@ type float64SliceParser []float64 func (i float64SliceParser) ParseEnv(key string, defaltVal any, options Parameters) any { sep := options.Separator - val := float64SliceOrDefault(key, defaltVal.([]float64), sep) + val := floatSliceOrDefaultGen(key, defaltVal.([]float64), sep) return val } @@ -297,7 +297,7 @@ type int8SliceParser []int8 func (i int8SliceParser) ParseEnv(key string, defaltVal any, options Parameters) any { sep := options.Separator - val := int8SliceOrDefault(key, defaltVal.([]int8), sep) + val := intSliceOrDefaultGen(key, defaltVal.([]int8), sep) return val } @@ -307,7 +307,7 @@ type int16SliceParser []int16 func (i int16SliceParser) ParseEnv(key string, defaltVal any, options Parameters) any { sep := options.Separator - val := int16SliceOrDefault(key, defaltVal.([]int16), sep) + val := intSliceOrDefaultGen(key, defaltVal.([]int16), sep) return val } @@ -317,7 +317,7 @@ type int32SliceParser []int32 func (i int32SliceParser) ParseEnv(key string, defaltVal any, options Parameters) any { sep := options.Separator - val := int32SliceOrDefault(key, defaltVal.([]int32), sep) + val := intSliceOrDefaultGen(key, defaltVal.([]int32), sep) return val } @@ -327,7 +327,7 @@ type int64SliceParser []int64 func (i int64SliceParser) ParseEnv(key string, defaltVal any, options Parameters) any { sep := options.Separator - val := int64SliceOrDefault(key, defaltVal.([]int64), sep) + val := intSliceOrDefaultGen(key, defaltVal.([]int64), sep) return val } @@ -408,7 +408,7 @@ type uint64SliceParser []uint64 func (i uint64SliceParser) ParseEnv(key string, defaltVal any, options Parameters) any { sep := options.Separator - val := uint64SliceOrDefault(key, defaltVal.([]uint64), sep) + val := uintSliceOrDefaultGen(key, defaltVal.([]uint64), sep) return val } @@ -434,7 +434,7 @@ type uintSliceParser []uint func (i uintSliceParser) ParseEnv(key string, defaltVal any, options Parameters) any { sep := options.Separator - val := uintSliceOrDefault(key, defaltVal.([]uint), sep) + val := uintSliceOrDefaultGen(key, defaltVal.([]uint), sep) return val } @@ -444,7 +444,7 @@ type uint8SliceParser []uint8 func (i uint8SliceParser) ParseEnv(key string, defaltVal any, options Parameters) any { sep := options.Separator - val := uint8SliceOrDefault(key, defaltVal.([]uint8), sep) + val := uintSliceOrDefaultGen(key, defaltVal.([]uint8), sep) return val } @@ -454,7 +454,7 @@ type uint32SliceParser []uint32 func (i uint32SliceParser) ParseEnv(key string, defaltVal any, options Parameters) any { sep := options.Separator - val := uint32SliceOrDefault(key, defaltVal.([]uint32), sep) + val := uintSliceOrDefaultGen(key, defaltVal.([]uint32), sep) return val } @@ -464,7 +464,7 @@ type uint16SliceParser []uint16 func (i uint16SliceParser) ParseEnv(key string, defaltVal any, options Parameters) any { sep := options.Separator - val := uint16SliceOrDefault(key, defaltVal.([]uint16), sep) + val := uintSliceOrDefaultGen(key, defaltVal.([]uint16), sep) return val } @@ -553,7 +553,7 @@ type uintptrSliceParser []uintptr func (i uintptrSliceParser) ParseEnv(key string, defaltVal any, options Parameters) any { sep := options.Separator - val := uintptrSliceOrDefault(key, defaltVal.([]uintptr), sep) + val := uintSliceOrDefaultGen(key, defaltVal.([]uintptr), sep) return val } @@ -573,7 +573,7 @@ type complex64SliceParser []complex64 func (i complex64SliceParser) ParseEnv(key string, defaltVal any, options Parameters) any { sep := options.Separator - val := complex64SliceOrDefault(key, defaltVal.([]complex64), sep) + val := complexSliceOrDefaultGen(key, defaltVal.([]complex64), sep) return val } @@ -593,7 +593,7 @@ type complex128SliceParser []complex128 func (i complex128SliceParser) ParseEnv(key string, defaltVal any, options Parameters) any { sep := options.Separator - val := complex128SliceOrDefault(key, defaltVal.([]complex128), sep) + val := complexSliceOrDefaultGen(key, defaltVal.([]complex128), sep) return val } diff --git a/internal/parsers.go b/internal/parsers.go index c310c209..22a80f77 100644 --- a/internal/parsers.go +++ b/internal/parsers.go @@ -9,23 +9,6 @@ import ( "time" ) -// intOrDefault retrieves the int value of the environment variable named -// by the key. -// If variable not set or value is empty - defaultVal will be returned. -func intOrDefault(key string, defaultVal int) int { - env := stringOrDefault(key, "") - if env == "" { - return defaultVal - } - - val, err := strconv.Atoi(env) - if err != nil { - return defaultVal - } - - return val -} - // stringOrDefault retrieves the string value of the environment variable named // by the key. // If variable not set or value is empty - defaultVal will be returned. @@ -96,274 +79,138 @@ func stringSliceOrDefault(key string, defaultVal []string, sep string) []string return val } -// intSliceOrDefault retrieves the int 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 intSliceOrDefault(key string, defaultVal []int, sep string) []int { - valraw := stringSliceOrDefault(key, nil, sep) - if valraw == nil { +func floatOrDefaultGen[T Float](key string, defaultVal T) T { + env := stringOrDefault(key, "") + if env == "" { return defaultVal } - val := make([]int, 0, len(valraw)) - - for _, s := range valraw { - v, err := strconv.Atoi(s) - if err != nil { - return defaultVal - } - - val = append(val, v) - } - - return val -} - -// float32SliceOrDefault retrieves the float32 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 float32SliceOrDefault(key string, defaultVal []float32, sep string) []float32 { - valraw := stringSliceOrDefault(key, nil, sep) - if valraw == nil { + val, err := parseFloatGen[T](env) + if err != nil { return defaultVal } - val := make([]float32, 0, len(valraw)) - - const ( - bitsize = 32 - ) - - for _, s := range valraw { - v, err := strconv.ParseFloat(s, bitsize) - if err != nil { - return defaultVal - } - - val = append(val, float32(v)) - } - return val } -// float64SliceOrDefault retrieves the float64 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 float64SliceOrDefault(key string, defaultVal []float64, sep string) []float64 { - valraw := stringSliceOrDefault(key, nil, sep) - if valraw == nil { - return defaultVal - } - - val := make([]float64, 0, len(valraw)) +func parseFloatGen[T Float](raw string) (T, error) { + var tt T const ( bitsize = 64 ) - for _, s := range valraw { - v, err := strconv.ParseFloat(s, bitsize) - if err != nil { - return defaultVal - } - - val = append(val, v) + val, err := strconv.ParseFloat(raw, bitsize) + if err != nil { + return tt, ErrInvalidValue } - return val + return any(T(val)).(T), nil } -// int64SliceOrDefault retrieves the int64 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 int64SliceOrDefault(key string, defaultVal []int64, sep string) []int64 { - valraw := stringSliceOrDefault(key, nil, sep) - if valraw == nil { - return defaultVal - } - - val := make([]int64, 0, len(valraw)) +func parseIntGen[T Int](raw string) (T, error) { + var tt T const ( - base = 10 - bitsize = 64 + base = 10 ) - for _, s := range valraw { - v, err := strconv.ParseInt(s, base, bitsize) - if err != nil { - return defaultVal - } + var ( + bitsize int + ) - val = append(val, v) + switch any(tt).(type) { + case int: + bitsize = 0 + case int8: + bitsize = 8 + case int16: + bitsize = 16 + case int32: + bitsize = 32 + case int64: + bitsize = 64 } - return val + val, err := strconv.ParseInt(raw, base, bitsize) + if err != nil { + return tt, ErrInvalidValue + } + + return any(T(val)).(T), nil } -// int8SliceOrDefault retrieves the int8 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 int8SliceOrDefault(key string, defaultVal []int8, sep string) []int8 { - valraw := stringSliceOrDefault(key, nil, sep) - if valraw == nil { +func intOrDefaultGen[T Int](key string, defaultVal T) T { + env := stringOrDefault(key, "") + if env == "" { return defaultVal } - val := make([]int8, 0, len(valraw)) - - const ( - base = 10 - bitsize = 8 - ) - - for _, s := range valraw { - v, err := strconv.ParseInt(s, base, bitsize) - if err != nil { - return defaultVal - } - - val = append(val, int8(v)) + val, err := parseIntGen[T](env) + if err != nil { + return defaultVal } return val } -func floatOrDefaultGen[T float32 | float64](key string, defaultVal T) T { - env := stringOrDefault(key, "") - if env == "" { - return defaultVal - } +func parseIntSliceGen[S []T, T Int](raw []string) (S, error) { + var tt S - const ( - bitsize = 64 - ) + val := make(S, 0, len(raw)) - var ( - castFn func(val float64) T - ) - - switch any(defaultVal).(type) { - case float32: - castFn = func(val float64) T { - return any(float32(val)).(T) - } - case float64: - castFn = func(val float64) T { - return any(val).(T) + for _, s := range raw { + v, err := parseIntGen[T](s) + if err != nil { + return tt, err } - } - val, err := strconv.ParseFloat(env, bitsize) - if err != nil { - return defaultVal + val = append(val, v) } - return castFn(val) + return val, nil } -func intOrDefaultGen[T int | int8 | int16 | int32 | int64](key string, defaultVal T) T { - env := stringOrDefault(key, "") - if env == "" { - return defaultVal - } - - const ( - base = 10 - ) +func parseFloatSliceGen[S []T, T Float](raw []string) (S, error) { + var tt S - var ( - bitsize int - castFn func(val int64) T - ) + val := make(S, 0, len(raw)) - switch any(defaultVal).(type) { - case int: - bitsize = 0 - castFn = func(val int64) T { - return any(int(val)).(T) - } - case int8: - bitsize = 8 - castFn = func(val int64) T { - return any(int8(val)).(T) - } - case int16: - bitsize = 16 - castFn = func(val int64) T { - return any(int16(val)).(T) - } - case int32: - bitsize = 32 - castFn = func(val int64) T { - return any(int32(val)).(T) - } - case int64: - bitsize = 64 - castFn = func(val int64) T { - return any(val).(T) + for _, s := range raw { + v, err := parseFloatGen[T](s) + if err != nil { + return tt, err } - } - val, err := strconv.ParseInt(env, base, bitsize) - if err != nil { - return defaultVal + val = append(val, v) } - return castFn(val) + return val, nil } -// int32SliceOrDefault retrieves the int32 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 int32SliceOrDefault(key string, defaultVal []int32, sep string) []int32 { +func floatSliceOrDefaultGen[S []T, T Float](key string, defaultVal S, sep string) S { valraw := stringSliceOrDefault(key, nil, sep) if valraw == nil { return defaultVal } - val := make([]int32, 0, len(valraw)) - - const ( - base = 10 - bitsize = 32 - ) - - for _, s := range valraw { - v, err := strconv.ParseInt(s, base, bitsize) - if err != nil { - return defaultVal - } - - val = append(val, int32(v)) + val, err := parseFloatSliceGen[S](valraw) + if err != nil { + return defaultVal } return val } -// int16SliceOrDefault retrieves the int16 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 int16SliceOrDefault(key string, defaultVal []int16, sep string) []int16 { +func intSliceOrDefaultGen[S []T, T Int](key string, defaultVal S, sep string) S { valraw := stringSliceOrDefault(key, nil, sep) if valraw == nil { return defaultVal } - val := make([]int16, 0, len(valraw)) - - const ( - base = 10 - bitsize = 16 - ) - - for _, s := range valraw { - v, err := strconv.ParseInt(s, base, bitsize) - if err != nil { - return defaultVal - } - - val = append(val, int16(v)) + val, err := parseIntSliceGen[S](valraw) + if err != nil { + return defaultVal } return val @@ -449,39 +296,8 @@ func durationSliceOrDefault(key string, defaultVal []time.Duration, separator st return val } -// uint64SliceOrDefault retrieves the uint64 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 uint64SliceOrDefault(key string, defaultVal []uint64, sep string) []uint64 { - valraw := stringSliceOrDefault(key, nil, sep) - if valraw == nil { - return defaultVal - } - - val := make([]uint64, 0, len(valraw)) - - const ( - base = 10 - bitsize = 64 - ) - - for _, s := range valraw { - v, err := strconv.ParseUint(s, base, bitsize) - if err != nil { - return defaultVal - } - - val = append(val, v) - } - - return val -} - -func uintOrDefaultGen[T uint | uint8 | uint16 | uint32 | uint64 | uintptr](key string, defaultVal T) T { - env := stringOrDefault(key, "") - if env == "" { - return defaultVal - } +func parseUintGen[T Uint](raw string) (T, error) { + var tt T const ( base = 10 @@ -489,157 +305,61 @@ func uintOrDefaultGen[T uint | uint8 | uint16 | uint32 | uint64 | uintptr](key s var ( bitsize int - castFn func(val uint64) T ) - switch any(defaultVal).(type) { + switch any(tt).(type) { case uint: bitsize = 0 - castFn = func(val uint64) T { - return any(uint(val)).(T) - } case uint8: bitsize = 8 - castFn = func(val uint64) T { - return any(uint8(val)).(T) - } case uint16: bitsize = 16 - castFn = func(val uint64) T { - return any(uint16(val)).(T) - } case uint32: bitsize = 32 - castFn = func(val uint64) T { - return any(uint32(val)).(T) - } case uint64: bitsize = 64 - castFn = func(val uint64) T { - return any(val).(T) - } + case uintptr: bitsize = 0 - castFn = func(val uint64) T { - return any(uintptr(val)).(T) - } } - val, err := strconv.ParseUint(env, base, bitsize) + val, err := strconv.ParseUint(raw, base, bitsize) if err != nil { - return defaultVal + return tt, ErrInvalidValue } - return castFn(val) + return any(T(val)).(T), nil } -// uintSliceOrDefault retrieves the uint 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 uintSliceOrDefault(key string, defaultVal []uint, sep string) []uint { - valraw := stringSliceOrDefault(key, nil, sep) - if valraw == nil { - return defaultVal - } - - val := make([]uint, 0, len(valraw)) - - const ( - base = 10 - bitsize = 32 - ) - - for _, s := range valraw { - v, err := strconv.ParseUint(s, base, bitsize) - if err != nil { - return defaultVal - } - - val = append(val, uint(v)) - } - - return val -} - -// uint8SliceOrDefault retrieves the uint8 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 uint8SliceOrDefault(key string, defaultVal []uint8, sep string) []uint8 { - valraw := stringSliceOrDefault(key, nil, sep) - if valraw == nil { +func uintOrDefaultGen[T Uint](key string, defaultVal T) T { + env := stringOrDefault(key, "") + if env == "" { return defaultVal } - val := make([]uint8, 0, len(valraw)) - - const ( - base = 10 - bitsize = 8 - ) - - for _, s := range valraw { - v, err := strconv.ParseUint(s, base, bitsize) - if err != nil { - return defaultVal - } - - val = append(val, uint8(v)) - } - - return val -} - -// uint16SliceOrDefault retrieves the uint16 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 uint16SliceOrDefault(key string, defaultVal []uint16, sep string) []uint16 { - valraw := stringSliceOrDefault(key, nil, sep) - if valraw == nil { + val, err := parseUintGen[T](env) + if err != nil { return defaultVal } - val := make([]uint16, 0, len(valraw)) - - const ( - base = 10 - bitsize = 16 - ) - - for _, s := range valraw { - v, err := strconv.ParseUint(s, base, bitsize) - if err != nil { - return defaultVal - } - - val = append(val, uint16(v)) - } - return val } -// uint32SliceOrDefault retrieves the uint32 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 uint32SliceOrDefault(key string, defaultVal []uint32, sep string) []uint32 { +func uintSliceOrDefaultGen[S []T, T Uint](key string, defaultVal S, sep string) S { valraw := stringSliceOrDefault(key, nil, sep) if valraw == nil { return defaultVal } - val := make([]uint32, 0, len(valraw)) - - const ( - base = 10 - bitsize = 32 - ) + val := make(S, 0, len(valraw)) for _, s := range valraw { - v, err := strconv.ParseUint(s, base, bitsize) + v, err := parseUintGen[T](s) if err != nil { return defaultVal } - val = append(val, uint32(v)) + val = append(val, v) } return val @@ -725,112 +445,52 @@ func ipSliceOrDefault(key string, defaultVal []net.IP, sep string) []net.IP { return val } -// uintptrSliceOrDefault retrieves the uintptr 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 uintptrSliceOrDefault(key string, defaultVal []uintptr, sep string) []uintptr { - valraw := stringSliceOrDefault(key, nil, sep) - if valraw == nil { - return defaultVal - } - - val := make([]uintptr, 0, len(valraw)) - - const ( - base = 10 - bitsize = 0 - ) - - for _, s := range valraw { - v, err := strconv.ParseUint(s, base, bitsize) - if err != nil { - return defaultVal - } - - val = append(val, uintptr(v)) - } - - return val -} - -func complexOrDefaultGen[T complex64 | complex128](key string, defaultVal T) T { - env := stringOrDefault(key, "") - if env == "" { - return defaultVal - } +func parseComplexGen[T Complex](raw string) (T, error) { + var tt T var ( bitsize int - castFn func(val complex128) T ) - switch any(defaultVal).(type) { + switch any(tt).(type) { case complex64: bitsize = 64 - - castFn = func(val complex128) T { - return any(complex64(val)).(T) - } case complex128: bitsize = 128 - - castFn = func(val complex128) T { - return any(val).(T) - } } - val, err := strconv.ParseComplex(env, bitsize) + val, err := strconv.ParseComplex(raw, bitsize) if err != nil { - return defaultVal + return tt, ErrInvalidValue } - return castFn(val) + return any(T(val)).(T), nil } -// complex64SliceOrDefault retrieves the complex64 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 complex64SliceOrDefault(key string, defaultVal []complex64, sep string) []complex64 { - valraw := stringSliceOrDefault(key, nil, sep) - if valraw == nil { +func complexOrDefaultGen[T complex64 | complex128](key string, defaultVal T) T { + env := stringOrDefault(key, "") + if env == "" { return defaultVal } - const ( - bitsize = 64 - ) - - val := make([]complex64, 0, len(valraw)) - - for _, s := range valraw { - v, err := strconv.ParseComplex(s, bitsize) - if err != nil { - return defaultVal - } - - val = append(val, complex64(v)) + val, err := parseComplexGen[T](env) + if err != nil { + return defaultVal } return val } -// complex128SliceOrDefault retrieves the complex128 slice value of the environment variable named -// by the key and separated by sep. -// If the variable is not set or the value is empty - defaultVal will be returned. -func complex128SliceOrDefault(key string, defaultVal []complex128, sep string) []complex128 { +func complexSliceOrDefaultGen[S []T, T Complex](key string, defaultVal S, sep string) S { valraw := stringSliceOrDefault(key, nil, sep) if valraw == nil { return defaultVal } - const ( - bitsize = 128 - ) - - val := make([]complex128, 0, len(valraw)) + val := make(S, 0, len(valraw)) for _, s := range valraw { - v, err := strconv.ParseComplex(s, bitsize) + v, err := parseComplexGen[T](s) if err != nil { return defaultVal } diff --git a/internal/parsers_test.go b/internal/parsers_test.go index 500a429d..7e7dac98 100644 --- a/internal/parsers_test.go +++ b/internal/parsers_test.go @@ -23,7 +23,7 @@ func Benchmark_float64SliceOrDefault(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _ = float64SliceOrDefault(testEnvKey, []float64{}, ",") + _ = floatSliceOrDefaultGen(testEnvKey, []float64{}, ",") } } @@ -97,7 +97,7 @@ func Test_intOrDefault(t *testing.T) { t.Run(tt.name, func(t *testing.T) { tt.precond.maybeSetEnv(t, tt.args.key) - got := intOrDefault(tt.args.key, tt.args.defaultVal) + got := intOrDefaultGen(tt.args.key, tt.args.defaultVal) assert.Equal(t, tt.expected.val, got) }) } @@ -898,7 +898,7 @@ func Test_intSliceOrDefault(t *testing.T) { t.Run(tt.name, func(t *testing.T) { tt.precond.maybeSetEnv(t, tt.args.key) - got := intSliceOrDefault(tt.args.key, tt.args.defaultVal, tt.args.sep) + got := intSliceOrDefaultGen(tt.args.key, tt.args.defaultVal, tt.args.sep) assert.Equal(t, tt.expected.val, got) }) } @@ -1029,7 +1029,7 @@ func Test_float32SliceOrDefault(t *testing.T) { t.Run(tt.name, func(t *testing.T) { tt.precond.maybeSetEnv(t, tt.args.key) - got := float32SliceOrDefault(tt.args.key, tt.args.defaultVal, tt.args.sep) + got := floatSliceOrDefaultGen(tt.args.key, tt.args.defaultVal, tt.args.sep) assert.Equal(t, tt.expected.val, got) }) } @@ -1160,7 +1160,7 @@ func Test_float64SliceOrDefault(t *testing.T) { t.Run(tt.name, func(t *testing.T) { tt.precond.maybeSetEnv(t, tt.args.key) - got := float64SliceOrDefault(tt.args.key, tt.args.defaultVal, tt.args.sep) + got := floatSliceOrDefaultGen(tt.args.key, tt.args.defaultVal, tt.args.sep) assert.Equal(t, tt.expected.val, got) }) } @@ -1290,7 +1290,7 @@ func Test_int16SliceOrDefault(t *testing.T) { t.Run(tt.name, func(t *testing.T) { tt.precond.maybeSetEnv(t, tt.args.key) - got := int16SliceOrDefault(tt.args.key, tt.args.defaultVal, tt.args.sep) + got := intSliceOrDefaultGen(tt.args.key, tt.args.defaultVal, tt.args.sep) assert.Equal(t, tt.expected.val, got) }) } @@ -1420,7 +1420,7 @@ func Test_int32SliceOrDefault(t *testing.T) { t.Run(tt.name, func(t *testing.T) { tt.precond.maybeSetEnv(t, tt.args.key) - got := int32SliceOrDefault(tt.args.key, tt.args.defaultVal, tt.args.sep) + got := intSliceOrDefaultGen(tt.args.key, tt.args.defaultVal, tt.args.sep) assert.Equal(t, tt.expected.val, got) }) } @@ -1550,7 +1550,7 @@ func Test_uintSliceOrDefault(t *testing.T) { t.Run(tt.name, func(t *testing.T) { tt.precond.maybeSetEnv(t, tt.args.key) - got := uintSliceOrDefault(tt.args.key, tt.args.defaultVal, tt.args.sep) + got := uintSliceOrDefaultGen(tt.args.key, tt.args.defaultVal, tt.args.sep) assert.Equal(t, tt.expected.val, got) }) } @@ -1680,7 +1680,7 @@ func Test_uint8SliceOrDefault(t *testing.T) { t.Run(tt.name, func(t *testing.T) { tt.precond.maybeSetEnv(t, tt.args.key) - got := uint8SliceOrDefault(tt.args.key, tt.args.defaultVal, tt.args.sep) + got := uintSliceOrDefaultGen(tt.args.key, tt.args.defaultVal, tt.args.sep) assert.Equal(t, tt.expected.val, got) }) } @@ -1810,7 +1810,7 @@ func Test_uint16SliceOrDefault(t *testing.T) { t.Run(tt.name, func(t *testing.T) { tt.precond.maybeSetEnv(t, tt.args.key) - got := uint16SliceOrDefault(tt.args.key, tt.args.defaultVal, tt.args.sep) + got := uintSliceOrDefaultGen(tt.args.key, tt.args.defaultVal, tt.args.sep) assert.Equal(t, tt.expected.val, got) }) } @@ -1940,7 +1940,7 @@ func Test_uint32SliceOrDefault(t *testing.T) { t.Run(tt.name, func(t *testing.T) { tt.precond.maybeSetEnv(t, tt.args.key) - got := uint32SliceOrDefault(tt.args.key, tt.args.defaultVal, tt.args.sep) + got := uintSliceOrDefaultGen(tt.args.key, tt.args.defaultVal, tt.args.sep) assert.Equal(t, tt.expected.val, got) }) } @@ -2070,7 +2070,7 @@ func Test_int8SliceOrDefault(t *testing.T) { t.Run(tt.name, func(t *testing.T) { tt.precond.maybeSetEnv(t, tt.args.key) - got := int8SliceOrDefault(tt.args.key, tt.args.defaultVal, tt.args.sep) + got := intSliceOrDefaultGen(tt.args.key, tt.args.defaultVal, tt.args.sep) assert.Equal(t, tt.expected.val, got) }) } @@ -2200,7 +2200,7 @@ func Test_int64SliceOrDefault(t *testing.T) { t.Run(tt.name, func(t *testing.T) { tt.precond.maybeSetEnv(t, tt.args.key) - got := int64SliceOrDefault(tt.args.key, tt.args.defaultVal, tt.args.sep) + got := intSliceOrDefaultGen(tt.args.key, tt.args.defaultVal, tt.args.sep) assert.Equal(t, tt.expected.val, got) }) } @@ -2911,7 +2911,7 @@ func Test_uint64SliceOrDefault(t *testing.T) { t.Run(tt.name, func(t *testing.T) { tt.precond.maybeSetEnv(t, tt.args.key) - got := uint64SliceOrDefault(tt.args.key, tt.args.defaultVal, tt.args.sep) + got := uintSliceOrDefaultGen(tt.args.key, tt.args.defaultVal, tt.args.sep) assert.Equal(t, tt.expected.val, got) }) } @@ -3865,7 +3865,7 @@ func Test_uintptrSliceOrDefault(t *testing.T) { t.Run(tt.name, func(t *testing.T) { tt.precond.maybeSetEnv(t, tt.args.key) - got := uintptrSliceOrDefault(tt.args.key, tt.args.defaultVal, tt.args.separator) + got := uintSliceOrDefaultGen(tt.args.key, tt.args.defaultVal, tt.args.separator) assert.Equal(t, tt.expected.val, got) }) } @@ -4057,7 +4057,7 @@ func Test_complex64SliceOrDefault(t *testing.T) { t.Run(tt.name, func(t *testing.T) { tt.precond.maybeSetEnv(t, tt.args.key) - got := complex64SliceOrDefault(tt.args.key, tt.args.defaultVal, tt.args.separator) + got := complexSliceOrDefaultGen(tt.args.key, tt.args.defaultVal, tt.args.separator) assert.Equal(t, tt.expected.val, got) }) } @@ -4249,7 +4249,7 @@ func Test_complex128SliceOrDefault(t *testing.T) { t.Run(tt.name, func(t *testing.T) { tt.precond.maybeSetEnv(t, tt.args.key) - got := complex128SliceOrDefault(tt.args.key, tt.args.defaultVal, tt.args.separator) + got := complexSliceOrDefaultGen(tt.args.key, tt.args.defaultVal, tt.args.separator) assert.Equal(t, tt.expected.val, got) }) } diff --git a/vendor/golang.org/x/exp/LICENSE b/vendor/golang.org/x/exp/LICENSE new file mode 100644 index 00000000..6a66aea5 --- /dev/null +++ b/vendor/golang.org/x/exp/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/exp/PATENTS b/vendor/golang.org/x/exp/PATENTS new file mode 100644 index 00000000..73309904 --- /dev/null +++ b/vendor/golang.org/x/exp/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/exp/constraints/constraints.go b/vendor/golang.org/x/exp/constraints/constraints.go new file mode 100644 index 00000000..2c033dff --- /dev/null +++ b/vendor/golang.org/x/exp/constraints/constraints.go @@ -0,0 +1,50 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package constraints defines a set of useful constraints to be used +// with type parameters. +package constraints + +// Signed is a constraint that permits any signed integer type. +// If future releases of Go add new predeclared signed integer types, +// this constraint will be modified to include them. +type Signed interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 +} + +// Unsigned is a constraint that permits any unsigned integer type. +// If future releases of Go add new predeclared unsigned integer types, +// this constraint will be modified to include them. +type Unsigned interface { + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr +} + +// Integer is a constraint that permits any integer type. +// If future releases of Go add new predeclared integer types, +// this constraint will be modified to include them. +type Integer interface { + Signed | Unsigned +} + +// Float is a constraint that permits any floating-point type. +// If future releases of Go add new predeclared floating-point types, +// this constraint will be modified to include them. +type Float interface { + ~float32 | ~float64 +} + +// Complex is a constraint that permits any complex numeric type. +// If future releases of Go add new predeclared complex numeric types, +// this constraint will be modified to include them. +type Complex interface { + ~complex64 | ~complex128 +} + +// Ordered is a constraint that permits any ordered type: any type +// that supports the operators < <= >= >. +// If future releases of Go add new ordered types, +// this constraint will be modified to include them. +type Ordered interface { + Integer | Float | ~string +} diff --git a/vendor/modules.txt b/vendor/modules.txt index ff50b88c..1967bf53 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -8,6 +8,9 @@ github.com/pmezard/go-difflib/difflib ## explicit; go 1.20 github.com/stretchr/testify/assert github.com/stretchr/testify/require +# golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 +## explicit; go 1.20 +golang.org/x/exp/constraints # gopkg.in/yaml.v3 v3.0.1 ## explicit gopkg.in/yaml.v3