diff --git a/.golangci.yaml b/.golangci.yaml index b74906f..de5dcf8 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -1,5 +1,6 @@ linters: disable: + - dupword - depguard presets: - bugs diff --git a/README.md b/README.md index ba266fb..5686816 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,20 @@ var ( fmt.Println(Red.Value.UI) ``` +If the enum has lots of members and new ones may be added over time, it's easy to forget to register all members in the enum. To prevent this, use enum.Builder to define an enum: + +```go +type Color enum.Member[string] + +var ( + b = enum.NewBuilder[string, Color]() + Red = b.Add(Color{"red"}) + Green = b.Add(Color{"green"}) + Blue = b.Add(Color{"blue"}) + Colors = b.Enum() +) +``` + ## 🤔 QnA 1. **What happens when enums are added in Go itself?** I'll keep it alive until someone uses it but I expect the project popularity to quickly die out when there is native language support for enums. When you can mess with the compiler itself, you can do more. For example, this package can't provide an exhaustiveness check for switch statements using enums (maybe only by implementing a linter) but proper language-level enums would most likely have it. diff --git a/enum.go b/enum.go index 140679a..79413ff 100644 --- a/enum.go +++ b/enum.go @@ -149,3 +149,35 @@ func (e Enum[M, V]) GoString() string { joined := strings.Join(values, ", ") return fmt.Sprintf("enum.New(%s)", joined) } + +// Builder is a constructor for an [Enum]. +// +// Use [Builder.Add] to add new members to the future enum +// and then call [Builder.Enum] to create a new [Enum] with all added members. +// +// Builder is useful for when you have lots of enum members, and new ones +// are added over time, as the project grows. In such scenario, it's easy to forget +// to add in the [Enum] a newly created [Member]. +// The builder is designed to prevent that. +type Builder[M iMember[V], V comparable] struct { + members []M + finished bool +} + +// NewBuilder creates a new [Builder], a constructor for an [Enum]. +func NewBuilder[V comparable, M iMember[V]]() Builder[M, V] { + return Builder[M, V]{make([]M, 0), false} +} + +// Add registers a new [Member] in the builder. +func (b *Builder[M, V]) Add(m M) M { + b.members = append(b.members, m) + return m +} + +// Enum creates a new [Enum] with all members registered using [Builder.Add]. +func (b *Builder[M, V]) Enum() Enum[M, V] { + b.finished = true + e := New(b.members...) + return e +} diff --git a/enum_test.go b/enum_test.go index b6e69aa..60bdbd1 100644 --- a/enum_test.go +++ b/enum_test.go @@ -112,3 +112,16 @@ func TestEnum_Index_Panic(t *testing.T) { }() Colors.Index(Color{"purple"}) } + +func TestBuilder(t *testing.T) { + is := is.New(t) + type Country enum.Member[string] + var ( + b = enum.NewBuilder[string, Country]() + NL = b.Add(Country{"Netherlands"}) + FR = b.Add(Country{"France"}) + BE = b.Add(Country{"Belgium"}) + Countries = b.Enum() + ) + is.Equal(Countries.Members(), []Country{NL, FR, BE}) +} diff --git a/example_test.go b/example_test.go index 7636269..30a86bf 100644 --- a/example_test.go +++ b/example_test.go @@ -182,3 +182,22 @@ func ExampleEnum_TypeName() { fmt.Println(tname) // Output: string } + +func ExampleNewBuilder() { + type Color enum.Member[string] + var ( + b = enum.NewBuilder[string, Color]() + Red = b.Add(Color{"red"}) + Green = b.Add(Color{"green"}) + Blue = b.Add(Color{"blue"}) + Colors = b.Enum() + ) + + fmt.Println( + Colors.Contains(Red), + Colors.Contains(Green), + Colors.Contains(Blue), + ) + // Output: + // true true true +}