From af06fab1f1450bbf81d0165f7cb43a64f1fe4e1e Mon Sep 17 00:00:00 2001 From: onyx-and-iris Date: Sat, 1 Aug 2026 04:18:48 +0100 Subject: [PATCH] add AllKinds to the public API. Use it as a single source of truth. --- kinds.go | 63 +++++++++++++++++++++++++++++++-------------- kinds_test.go | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 19 deletions(-) create mode 100644 kinds_test.go diff --git a/kinds.go b/kinds.go index ea631f6..696b2a6 100644 --- a/kinds.go +++ b/kinds.go @@ -1,6 +1,10 @@ package lottery -import "fmt" +import ( + "fmt" + "slices" + "strings" +) type Kind string @@ -12,22 +16,43 @@ const ( KindPowerball Kind = "powerball" ) -func ParseKind(kind string) (Kind, error) { - switch kind { - case "lotto": - return KindLotto, nil - case "euromillions": - return KindEuroMillions, nil - case "setforlife": - return KindSetForLife, nil - case "thunderball": - return KindThunderball, nil - case "powerball": - return KindPowerball, nil - default: - return "", fmt.Errorf( - "invalid lottery kind: %s, must be one of: lotto, euromillions, setforlife, thunderball, powerball", - kind, - ) - } +var allKinds = []Kind{ + KindLotto, + KindEuroMillions, + KindSetForLife, + KindThunderball, + KindPowerball, +} + +var allKindsText = []string{ + string(KindLotto), + string(KindEuroMillions), + string(KindSetForLife), + string(KindThunderball), + string(KindPowerball), +} + +var allKindsCSV = strings.Join(allKindsText, ", ") + +// AllKinds returns the complete, ordered list of supported lottery kinds. +// +// The returned slice is a copy and can be modified safely by callers. +func AllKinds() []Kind { + kinds := make([]Kind, len(allKinds)) + copy(kinds, allKinds) + return kinds +} + +// ParseKind parses a string into a Kind, returning an error if the string is not a valid kind. +func ParseKind(kind string) (Kind, error) { + parsed := Kind(kind) + if slices.Contains(allKinds, parsed) { + return parsed, nil + } + + return "", fmt.Errorf( + "invalid lottery kind: %s, must be one of: %s", + kind, + allKindsCSV, + ) } diff --git a/kinds_test.go b/kinds_test.go new file mode 100644 index 0000000..e9dd100 --- /dev/null +++ b/kinds_test.go @@ -0,0 +1,71 @@ +package lottery + +import ( + "strings" + "testing" +) + +func TestAllKindsReturnsExpectedOrder(t *testing.T) { + t.Parallel() + + got := AllKinds() + want := []Kind{ + KindLotto, + KindEuroMillions, + KindSetForLife, + KindThunderball, + KindPowerball, + } + + if len(got) != len(want) { + t.Fatalf("expected %d kinds, got %d", len(want), len(got)) + } + + for i := range want { + if got[i] != want[i] { + t.Fatalf("kind at index %d: expected %q, got %q", i, want[i], got[i]) + } + } +} + +func TestAllKindsReturnsCopy(t *testing.T) { + t.Parallel() + + first := AllKinds() + first[0] = "mutated" + + again := AllKinds() + if again[0] != KindLotto { + t.Fatalf("expected AllKinds to return a defensive copy") + } +} + +func TestParseKindAcceptsAllKinds(t *testing.T) { + t.Parallel() + + for _, kind := range AllKinds() { + parsed, err := ParseKind(string(kind)) + if err != nil { + t.Fatalf("expected no error for %q, got %v", kind, err) + } + if parsed != kind { + t.Fatalf("expected %q, got %q", kind, parsed) + } + } +} + +func TestParseKindIncludesAllKindsInError(t *testing.T) { + t.Parallel() + + _, err := ParseKind("invalid") + if err == nil { + t.Fatalf("expected an error for invalid kind") + } + + message := err.Error() + for _, kind := range AllKinds() { + if !strings.Contains(message, string(kind)) { + t.Fatalf("expected error message to contain kind %q: %s", kind, message) + } + } +}