From 817bdea525feb6eb2e2c588877bafc9534a6eea5 Mon Sep 17 00:00:00 2001 From: Virgil Date: Thu, 2 Apr 2026 10:24:05 +0000 Subject: [PATCH] fix(cli): make legacy selection errors actionable Co-Authored-By: Virgil --- pkg/cli/prompt.go | 45 ++++++++++-------------------------------- pkg/cli/prompt_test.go | 10 ++++++++++ 2 files changed, 20 insertions(+), 35 deletions(-) diff --git a/pkg/cli/prompt.go b/pkg/cli/prompt.go index 9c3e207..8f9d8d7 100644 --- a/pkg/cli/prompt.go +++ b/pkg/cli/prompt.go @@ -73,7 +73,7 @@ func Select(label string, options []string) (string, error) { n, err := strconv.Atoi(strings.TrimSpace(input)) if err != nil || n < 1 || n > len(options) { - return "", errors.New("invalid selection") + return "", fmt.Errorf("invalid selection: choose a number between 1 and %d", len(options)) } return options[n-1], nil } @@ -99,39 +99,14 @@ func MultiSelect(label string, options []string) ([]string, error) { return nil, err } - var ( - selected []string - seen = make(map[int]bool, len(options)) - ) - normalized := strings.NewReplacer(",", " ").Replace(input) - for _, s := range strings.Fields(normalized) { - if strings.Contains(s, "-") { - parts := strings.SplitN(s, "-", 2) - if len(parts) != 2 { - continue - } - start, startErr := strconv.Atoi(parts[0]) - end, endErr := strconv.Atoi(parts[1]) - if startErr != nil || endErr != nil || start < 1 || end > len(options) || start > end { - continue - } - for n := start; n <= end; n++ { - if !seen[n-1] { - seen[n-1] = true - selected = append(selected, options[n-1]) - } - } - continue - } - - n, convErr := strconv.Atoi(s) - if convErr != nil || n < 1 || n > len(options) { - continue - } - if !seen[n-1] { - seen[n-1] = true - selected = append(selected, options[n-1]) - } + selected, parseErr := parseMultiSelection(strings.TrimSpace(input), len(options)) + if parseErr != nil { + return nil, fmt.Errorf("invalid selection: %w", parseErr) } - return selected, nil + + selectedOptions := make([]string, 0, len(selected)) + for _, idx := range selected { + selectedOptions = append(selectedOptions, options[idx]) + } + return selectedOptions, nil } diff --git a/pkg/cli/prompt_test.go b/pkg/cli/prompt_test.go index 7459e65..b1b9bb5 100644 --- a/pkg/cli/prompt_test.go +++ b/pkg/cli/prompt_test.go @@ -51,6 +51,7 @@ func TestSelect_Bad_Invalid(t *testing.T) { _, err := Select("Pick", []string{"a", "b"}) assert.Error(t, err) + assert.Contains(t, err.Error(), "choose a number between 1 and 2") } func TestSelect_Bad_EOF(t *testing.T) { @@ -103,6 +104,15 @@ func TestMultiSelect_Good_DedupesSelections(t *testing.T) { assert.Equal(t, []string{"a", "b", "c"}, vals) } +func TestMultiSelect_Bad_InvalidInput(t *testing.T) { + SetStdin(strings.NewReader("1 foo\n")) + defer SetStdin(nil) + + _, err := MultiSelect("Pick", []string{"a", "b", "c"}) + assert.Error(t, err) + assert.Contains(t, err.Error(), "invalid selection") +} + func TestMultiSelect_Good_EmptyOptions(t *testing.T) { vals, err := MultiSelect("Pick", nil) assert.NoError(t, err) -- 2.45.3