fix(cli): harden legacy select helpers
All checks were successful
Security Scan / security (push) Successful in 21s
All checks were successful
Security Scan / security (push) Successful in 21s
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
aa537c89ca
commit
5c8f08b60e
2 changed files with 41 additions and 3 deletions
|
|
@ -55,6 +55,10 @@ func Prompt(label, defaultVal string) (string, error) {
|
||||||
|
|
||||||
// Select presents numbered options and returns the selected value.
|
// Select presents numbered options and returns the selected value.
|
||||||
func Select(label string, options []string) (string, error) {
|
func Select(label string, options []string) (string, error) {
|
||||||
|
if len(options) == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Println(compileGlyphs(label))
|
fmt.Println(compileGlyphs(label))
|
||||||
for i, opt := range options {
|
for i, opt := range options {
|
||||||
fmt.Printf(" %d. %s\n", i+1, compileGlyphs(opt))
|
fmt.Printf(" %d. %s\n", i+1, compileGlyphs(opt))
|
||||||
|
|
@ -76,6 +80,10 @@ func Select(label string, options []string) (string, error) {
|
||||||
|
|
||||||
// MultiSelect presents checkboxes (space-separated numbers).
|
// MultiSelect presents checkboxes (space-separated numbers).
|
||||||
func MultiSelect(label string, options []string) ([]string, error) {
|
func MultiSelect(label string, options []string) ([]string, error) {
|
||||||
|
if len(options) == 0 {
|
||||||
|
return []string{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Println(compileGlyphs(label))
|
fmt.Println(compileGlyphs(label))
|
||||||
for i, opt := range options {
|
for i, opt := range options {
|
||||||
fmt.Printf(" %d. %s\n", i+1, compileGlyphs(opt))
|
fmt.Printf(" %d. %s\n", i+1, compileGlyphs(opt))
|
||||||
|
|
@ -91,7 +99,10 @@ func MultiSelect(label string, options []string) ([]string, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var selected []string
|
var (
|
||||||
|
selected []string
|
||||||
|
seen = make(map[int]bool, len(options))
|
||||||
|
)
|
||||||
normalized := strings.NewReplacer(",", " ").Replace(input)
|
normalized := strings.NewReplacer(",", " ").Replace(input)
|
||||||
for _, s := range strings.Fields(normalized) {
|
for _, s := range strings.Fields(normalized) {
|
||||||
if strings.Contains(s, "-") {
|
if strings.Contains(s, "-") {
|
||||||
|
|
@ -105,7 +116,10 @@ func MultiSelect(label string, options []string) ([]string, error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for n := start; n <= end; n++ {
|
for n := start; n <= end; n++ {
|
||||||
selected = append(selected, options[n-1])
|
if !seen[n-1] {
|
||||||
|
seen[n-1] = true
|
||||||
|
selected = append(selected, options[n-1])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -114,7 +128,10 @@ func MultiSelect(label string, options []string) ([]string, error) {
|
||||||
if convErr != nil || n < 1 || n > len(options) {
|
if convErr != nil || n < 1 || n > len(options) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
selected = append(selected, options[n-1])
|
if !seen[n-1] {
|
||||||
|
seen[n-1] = true
|
||||||
|
selected = append(selected, options[n-1])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return selected, nil
|
return selected, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,12 @@ func TestSelect_Bad_EOF(t *testing.T) {
|
||||||
assert.ErrorIs(t, err, io.EOF)
|
assert.ErrorIs(t, err, io.EOF)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSelect_Good_EmptyOptions(t *testing.T) {
|
||||||
|
val, err := Select("Pick", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Empty(t, val)
|
||||||
|
}
|
||||||
|
|
||||||
func TestMultiSelect_Good(t *testing.T) {
|
func TestMultiSelect_Good(t *testing.T) {
|
||||||
SetStdin(strings.NewReader("1 3\n"))
|
SetStdin(strings.NewReader("1 3\n"))
|
||||||
defer SetStdin(nil)
|
defer SetStdin(nil)
|
||||||
|
|
@ -88,6 +94,21 @@ func TestMultiSelect_Bad_EOFReturnsEmptySelection(t *testing.T) {
|
||||||
assert.Empty(t, vals)
|
assert.Empty(t, vals)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMultiSelect_Good_DedupesSelections(t *testing.T) {
|
||||||
|
SetStdin(strings.NewReader("1 1 2-3 2\n"))
|
||||||
|
defer SetStdin(nil)
|
||||||
|
|
||||||
|
vals, err := MultiSelect("Pick", []string{"a", "b", "c"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, []string{"a", "b", "c"}, vals)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultiSelect_Good_EmptyOptions(t *testing.T) {
|
||||||
|
vals, err := MultiSelect("Pick", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Empty(t, vals)
|
||||||
|
}
|
||||||
|
|
||||||
func TestConfirm_Good(t *testing.T) {
|
func TestConfirm_Good(t *testing.T) {
|
||||||
SetStdin(strings.NewReader("y\n"))
|
SetStdin(strings.NewReader("y\n"))
|
||||||
defer SetStdin(nil)
|
defer SetStdin(nil)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue