diff --git a/README.md b/README.md index f173d05..4b05f62 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ enumeration values either with a single flag `--mode=foo,bar` or multiple flag calls, such as `--mode=foo --mode=bar`. Application programmers then simply deal with enumeration values in form of -uints (or ints, _erm_, anything that satisfies `constraints.Integer`s), +uints (or ints, _erm_, anything that satisfies `constraints.Ordered`s), liberated from parsing strings and validating enumeration flags. For devcontainer instructions, please see the [section "DevContainer" @@ -76,7 +76,7 @@ import ( ) // ① Define your new enum flag type. It can be derived from enumflag.Flag, -// but it doesn't need to be as long as it satisfies constraints.Integer. +// but it doesn't need to be as long as it satisfies constraints.Ordered. type FooMode enumflag.Flag // ② Define the enumeration values for FooMode. @@ -306,7 +306,7 @@ import ( ) // ① Define your new enum flag type. It can be derived from enumflag.Flag, -// but it doesn't need to be as long as it satisfies constraints.Integer. +// but it doesn't need to be as long as it satisfies constraints.Ordered. type MooMode enumflag.Flag // ② Define the enumeration values for FooMode. diff --git a/completion.go b/completion.go index 8ee0121..a4dcb03 100644 --- a/completion.go +++ b/completion.go @@ -24,7 +24,7 @@ import ( // value prefix. The reason is that enumflag will automatically register the // correct (erm, “complete”) completion text. Please note that it isn't // necessary to supply any help texts in order to register enum flag completion. -type Help[E constraints.Integer] map[E]string +type Help[E constraints.Ordered] map[E]string // Completor tells cobra how to complete a flag. See also cobra's [dynamic flag // completion] documentation. diff --git a/example_nodefault_test.go b/example_nodefault_test.go index ab2b6c0..040fd2b 100644 --- a/example_nodefault_test.go +++ b/example_nodefault_test.go @@ -4,11 +4,12 @@ import ( "fmt" "github.com/spf13/cobra" + "github.com/thediveo/enumflag/v2" ) // ① Define your new enum flag type. It can be derived from enumflag.Flag, -// but it doesn't need to be as long as it satisfies constraints.Integer. +// but it doesn't need to be as long as it satisfies constraints.Ordered. type BarMode enumflag.Flag // ② Define the enumeration values for BarMode. diff --git a/example_slice_test.go b/example_slice_test.go index 3bae3cd..3e77ca3 100644 --- a/example_slice_test.go +++ b/example_slice_test.go @@ -4,11 +4,12 @@ import ( "fmt" "github.com/spf13/cobra" + "github.com/thediveo/enumflag/v2" ) // ① Define your new enum flag type. It can be derived from enumflag.Flag, -// but it doesn't need to be as long as it satisfies constraints.Integer. +// but it doesn't need to be as long as it satisfies constraints.Ordered. type MooMode enumflag.Flag // ② Define the enumeration values for FooMode. diff --git a/example_test.go b/example_test.go index 33e31e7..b3a204c 100644 --- a/example_test.go +++ b/example_test.go @@ -4,11 +4,12 @@ import ( "fmt" "github.com/spf13/cobra" + "github.com/thediveo/enumflag/v2" ) // ① Define your new enum flag type. It can be derived from enumflag.Flag, -// but it doesn't need to be as long as it satisfies constraints.Integer. +// but it doesn't need to be as long as it satisfies constraints.Ordered. type FooMode enumflag.Flag // ② Define the enumeration values for FooMode. diff --git a/flag.go b/flag.go index bd58dd4..7db757e 100644 --- a/flag.go +++ b/flag.go @@ -31,7 +31,7 @@ import ( // However, applications don't need to base their own enum types on Flag. The // only requirement for user-defined enumeration flags is that they must be // (“somewhat”) compatible with the Flag type, or more precise: user-defined -// enumerations must satisfy [constraints.Integer]. +// enumerations must satisfy [constraints.Ordered]. type Flag uint // EnumCaseSensitivity specifies whether the textual representations of enum @@ -46,11 +46,11 @@ const ( ) // EnumFlagValue wraps a user-defined enum type value satisfying -// [constraints.Integer] or [][constraints.Integer]. It implements the +// [constraints.Ordered] or [][constraints.Ordered]. It implements the // [github.com/spf13/pflag.Value] interface, so the user-defined enum type value // can directly be used with the fine pflag drop-in package for Golang CLI // flags. -type EnumFlagValue[E constraints.Integer] struct { +type EnumFlagValue[E constraints.Ordered] struct { value enumValue[E] // enum value of a user-defined enum scalar or slice type. enumtype string // user-friendly name of the user-defined enum type. names enumMapper[E] // enum value names. @@ -63,26 +63,26 @@ type EnumFlagValue[E constraints.Integer] struct { // code”: by just moving the interface type from the source file with the struct // types to the source file with the consumer we achieve immediate Go // perfectness! Strike! -type enumValue[E constraints.Integer] interface { +type enumValue[E constraints.Ordered] interface { Get() any Set(val string, names enumMapper[E]) error String(names enumMapper[E]) string NewCompletor(enums EnumIdentifiers[E], help Help[E]) Completor } -// New wraps a given enum variable (satisfying [constraints.Integer]) so that it +// New wraps a given enum variable (satisfying [constraints.Ordered]) so that it // can be used as a flag Value with [github.com/spf13/pflag.Var] and // [github.com/spf13/pflag.VarP]. In case no default enum value should be set // and therefore no default shown in [spf13/cobra], use [NewWithoutDefault] // instead. // // [spf13/cobra]: https://github.com/spf13/cobra -func New[E constraints.Integer](flag *E, typename string, mapping EnumIdentifiers[E], sensitivity EnumCaseSensitivity) *EnumFlagValue[E] { +func New[E constraints.Ordered](flag *E, typename string, mapping EnumIdentifiers[E], sensitivity EnumCaseSensitivity) *EnumFlagValue[E] { return new("New", flag, typename, mapping, sensitivity, false) } // NewWithoutDefault wraps a given enum variable (satisfying -// [constraints.Integer]) so that it can be used as a flag Value with +// [constraints.Ordered]) so that it can be used as a flag Value with // [github.com/spf13/pflag.Var] and [github.com/spf13/pflag.VarP]. Please note // that the zero enum value must not be mapped and thus not be assigned to any // enum value textual representation. @@ -91,14 +91,14 @@ func New[E constraints.Integer](flag *E, typename string, mapping EnumIdentifier // created with NewWithoutDefault. // // [spf13/cobra]: https://github.com/spf13/cobra -func NewWithoutDefault[E constraints.Integer](flag *E, typename string, mapping EnumIdentifiers[E], sensitivity EnumCaseSensitivity) *EnumFlagValue[E] { +func NewWithoutDefault[E constraints.Ordered](flag *E, typename string, mapping EnumIdentifiers[E], sensitivity EnumCaseSensitivity) *EnumFlagValue[E] { return new("NewWithoutDefault", flag, typename, mapping, sensitivity, true) } // new returns a new enum variable to be used with pflag.Var and pflag.VarP. -func new[E constraints.Integer](ctor string, flag *E, typename string, mapping EnumIdentifiers[E], sensitivity EnumCaseSensitivity, nodefault bool) *EnumFlagValue[E] { +func new[E constraints.Ordered](ctor string, flag *E, typename string, mapping EnumIdentifiers[E], sensitivity EnumCaseSensitivity, nodefault bool) *EnumFlagValue[E] { if flag == nil { - panic(fmt.Sprintf("%s requires flag to be a non-nil pointer to an enum value satisfying constraints.Integer", ctor)) + panic(fmt.Sprintf("%s requires flag to be a non-nil pointer to an enum value satisfying constraints.Ordered", ctor)) } if mapping == nil { panic(fmt.Sprintf("%s requires mapping not to be nil", ctor)) @@ -110,12 +110,12 @@ func new[E constraints.Integer](ctor string, flag *E, typename string, mapping E } } -// NewSlice wraps a given enum slice variable (satisfying [constraints.Integer]) +// NewSlice wraps a given enum slice variable (satisfying [constraints.Ordered]) // so that it can be used as a flag Value with [github.com/spf13/pflag.Var] and // [github.com/spf13/pflag.VarP]. -func NewSlice[E constraints.Integer](flag *[]E, typename string, mapping EnumIdentifiers[E], sensitivity EnumCaseSensitivity) *EnumFlagValue[E] { +func NewSlice[E constraints.Ordered](flag *[]E, typename string, mapping EnumIdentifiers[E], sensitivity EnumCaseSensitivity) *EnumFlagValue[E] { if flag == nil { - panic("NewSlice requires flag to be a non-nil pointer to an enum value slice satisfying []constraints.Integer") + panic("NewSlice requires flag to be a non-nil pointer to an enum value slice satisfying []any") } if mapping == nil { panic("NewSlice requires mapping not to be nil") diff --git a/mapper.go b/mapper.go index 4c0a75d..86f1b14 100644 --- a/mapper.go +++ b/mapper.go @@ -29,18 +29,18 @@ import ( // representation (identifier). If more than one textual representation exists // for the same enumeration value, then the first textual representation is // considered to be the canonical one. -type EnumIdentifiers[E constraints.Integer] map[E][]string +type EnumIdentifiers[E constraints.Ordered] map[E][]string // enumMapper is an optionally case insensitive map from enum values to their // corresponding textual representations. -type enumMapper[E constraints.Integer] struct { +type enumMapper[E constraints.Ordered] struct { m EnumIdentifiers[E] sensitivity EnumCaseSensitivity } // newEnumMapper returns a new enumMapper for the given mapping and case // sensitivity or insensitivity. -func newEnumMapper[E constraints.Integer](mapping EnumIdentifiers[E], sensitivity EnumCaseSensitivity) enumMapper[E] { +func newEnumMapper[E constraints.Ordered](mapping EnumIdentifiers[E], sensitivity EnumCaseSensitivity) enumMapper[E] { return enumMapper[E]{ m: mapping, sensitivity: sensitivity, @@ -81,7 +81,8 @@ func (m enumMapper[E]) ValueOf(name string) (E, error) { allids = append(allids, strings.Join(s, "/")) } sort.Strings(allids) - return 0, fmt.Errorf("must be %s", strings.Join(allids, ", ")) + var zero E + return zero, fmt.Errorf("must be %s", strings.Join(allids, ", ")) } // Mapping returns the mapping of enum values to their names. diff --git a/value_scalar.go b/value_scalar.go index 9843c5e..bbf5f2f 100644 --- a/value_scalar.go +++ b/value_scalar.go @@ -26,7 +26,7 @@ const unknown = "" // enumScalar represents a mutable, single enumeration value that can be // retrieved, set, and stringified. -type enumScalar[E constraints.Integer] struct { +type enumScalar[E constraints.Ordered] struct { v *E nodefault bool // opts in to accepting a zero enum value as the "none" } @@ -60,7 +60,8 @@ func (s *enumScalar[E]) String(names enumMapper[E]) string { if ids := names.Lookup(*s.v); len(ids) > 0 { return ids[0] } - if *s.v == 0 && s.nodefault { + var zero E + if *s.v == zero && s.nodefault { return "" } return unknown diff --git a/value_slice.go b/value_slice.go index 88e3d29..2ec54c3 100644 --- a/value_slice.go +++ b/value_slice.go @@ -24,7 +24,7 @@ import ( // enumSlice represents a slice of enumeration values that can be retrieved, // set, and stringified. -type enumSlice[E constraints.Integer] struct { +type enumSlice[E constraints.Ordered] struct { v *[]E merge bool // replace the complete slice or merge values? }