Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions ui/color_ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,12 @@ func (ui *ColorUI) PrintTable(table Table) {
ui.parent.PrintTable(table)
}

func (ui *ColorUI) AskForText(label string) (string, error) {
return ui.parent.AskForText(label)
func (ui *ColorUI) AskForText(opts TextOpts) (string, error) {
return ui.parent.AskForText(opts)
}

func (ui *ColorUI) AskForChoice(label string, options []string) (int, error) {
return ui.parent.AskForChoice(label, options)
func (ui *ColorUI) AskForChoice(opts ChoiceOpts) (int, error) {
return ui.parent.AskForChoice(opts)
}

func (ui *ColorUI) AskForPassword(label string) (string, error) {
Expand Down
8 changes: 4 additions & 4 deletions ui/conf_ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,12 @@ func (ui *ConfUI) PrintTable(table Table) {
ui.parent.PrintTable(table)
}

func (ui *ConfUI) AskForText(label string) (string, error) {
return ui.parent.AskForText(label)
func (ui *ConfUI) AskForText(opts TextOpts) (string, error) {
return ui.parent.AskForText(opts)
}

func (ui *ConfUI) AskForChoice(label string, options []string) (int, error) {
return ui.parent.AskForChoice(label, options)
func (ui *ConfUI) AskForChoice(opts ChoiceOpts) (int, error) {
return ui.parent.AskForChoice(opts)
}

func (ui *ConfUI) AskForPassword(label string) (string, error) {
Expand Down
11 changes: 6 additions & 5 deletions ui/fakes/fake_ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"sync"

types "github.com/cppforlife/go-cli-ui/ui"
. "github.com/cppforlife/go-cli-ui/ui/table"
)

Expand Down Expand Up @@ -93,24 +94,24 @@ func (ui *FakeUI) PrintTable(table Table) {
ui.Tables = append(ui.Tables, table)
}

func (ui *FakeUI) AskForText(label string) (string, error) {
func (ui *FakeUI) AskForText(opts types.TextOpts) (string, error) {
ui.mutex.Lock()
defer ui.mutex.Unlock()

ui.AskedTextLabels = append(ui.AskedTextLabels, label)
ui.AskedTextLabels = append(ui.AskedTextLabels, opts.Label)
answer := ui.AskedText[0]
ui.AskedText = ui.AskedText[1:]
return answer.Text, answer.Error
}

func (ui *FakeUI) AskForChoice(label string, options []string) (int, error) {
func (ui *FakeUI) AskForChoice(opts types.ChoiceOpts) (int, error) {
ui.mutex.Lock()
defer ui.mutex.Unlock()

ui.AskedChoiceCalled = true

ui.AskedChoiceLabel = label
ui.AskedChoiceOptions = options
ui.AskedChoiceLabel = opts.Label
ui.AskedChoiceOptions = opts.Choices

chosen := ui.AskedChoiceChosens[0]
ui.AskedChoiceChosens = ui.AskedChoiceChosens[1:]
Expand Down
8 changes: 4 additions & 4 deletions ui/indenting_ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ func (ui *IndentingUI) PrintTable(table Table) {
ui.parent.PrintTable(table)
}

func (ui *IndentingUI) AskForText(label string) (string, error) {
return ui.parent.AskForText(label)
func (ui *IndentingUI) AskForText(opts TextOpts) (string, error) {
return ui.parent.AskForText(opts)
}

func (ui *IndentingUI) AskForChoice(label string, options []string) (int, error) {
return ui.parent.AskForChoice(label, options)
func (ui *IndentingUI) AskForChoice(opts ChoiceOpts) (int, error) {
return ui.parent.AskForChoice(opts)
}

func (ui *IndentingUI) AskForPassword(label string) (string, error) {
Expand Down
4 changes: 2 additions & 2 deletions ui/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ type UI interface {

PrintTable(Table)

AskForText(label string) (string, error)
AskForChoice(label string, options []string) (int, error)
AskForText(opts TextOpts) (string, error)
AskForChoice(opts ChoiceOpts) (int, error)
AskForPassword(label string) (string, error)

// AskForConfirmation returns error if user doesnt want to continue
Expand Down
4 changes: 2 additions & 2 deletions ui/json_ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,11 @@ func (ui *JSONUI) PrintTable(table Table) {
ui.uiResp.Tables = append(ui.uiResp.Tables, resp)
}

func (ui *JSONUI) AskForText(_ string) (string, error) {
func (ui *JSONUI) AskForText(_ TextOpts) (string, error) {
panic("Cannot ask for input in JSON UI")
}

func (ui *JSONUI) AskForChoice(_ string, _ []string) (int, error) {
func (ui *JSONUI) AskForChoice(_ ChoiceOpts) (int, error) {
panic("Cannot ask for a choice in JSON UI")
}

Expand Down
4 changes: 2 additions & 2 deletions ui/json_ui_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ func TestJSONUI(t *testing.T) {
parentUI := &fakeui.FakeUI{}
ui := NewJSONUI(parentUI, NewRecordingLogger())

assert.Panics(t, func() { ui.AskForText("") })
assert.Panics(t, func() { ui.AskForText(TextOpts{}) })
})
})

Expand All @@ -328,7 +328,7 @@ func TestJSONUI(t *testing.T) {
parentUI := &fakeui.FakeUI{}
ui := NewJSONUI(parentUI, NewRecordingLogger())

assert.Panics(t, func() { ui.AskForChoice("", nil) })
assert.Panics(t, func() { ui.AskForChoice(ChoiceOpts{}) })
})
})

Expand Down
19 changes: 15 additions & 4 deletions ui/non_interactive.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package ui

import (
"fmt"

. "github.com/cppforlife/go-cli-ui/ui/table"
)

Expand Down Expand Up @@ -40,12 +42,21 @@ func (ui *NonInteractiveUI) PrintTable(table Table) {
ui.parent.PrintTable(table)
}

func (ui *NonInteractiveUI) AskForText(label string) (string, error) {
panic("Cannot ask for input in non-interactive UI")
func (ui *NonInteractiveUI) AskForText(opts TextOpts) (string, error) {
if opts.ValidateFunc != nil {
isValid, message, err := opts.ValidateFunc(opts.Default)
if err != nil || !isValid {
return "", fmt.Errorf("Validation error: %s", message)
}
}
return opts.Default, nil
}

func (ui *NonInteractiveUI) AskForChoice(label string, options []string) (int, error) {
panic("Cannot ask for a choice in non-interactive UI")
func (ui *NonInteractiveUI) AskForChoice(opts ChoiceOpts) (int, error) {
if opts.Default >= len(opts.Choices) || opts.Default < 0 {
return 0, fmt.Errorf("Default value should be index and must be in (0-%d)", len(opts.Choices)-1)
}
return opts.Default, nil
}

func (ui *NonInteractiveUI) AskForPassword(label string) (string, error) {
Expand Down
46 changes: 42 additions & 4 deletions ui/non_interactive_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ui_test

import (
"fmt"
"testing"

. "github.com/cppforlife/go-cli-ui/ui"
Expand Down Expand Up @@ -87,11 +88,41 @@ func TestNonInteractiveUI(t *testing.T) {
})

t.Run("AskForText", func(t *testing.T) {
t.Run("panics", func(t *testing.T) {
t.Run("default non empty", func(t *testing.T) {
parentUI := &fakeui.FakeUI{}
ui := NewNonInteractiveUI(parentUI)

assert.Panics(t, func() { ui.AskForText("") })
text, err := ui.AskForText(TextOpts{
Label: "",
Default: "foo",
ValidateFunc: func(s string) (bool, string, error) {
if s == "" {
return false, "", fmt.Errorf("should not be empty")
}
return true, "", nil
},
})

assert.Equal(t, text, "foo")
assert.Nil(t, err)
})
t.Run("default empty", func(t *testing.T) {
parentUI := &fakeui.FakeUI{}
ui := NewNonInteractiveUI(parentUI)

text, err := ui.AskForText(TextOpts{
Label: "",
Default: "",
ValidateFunc: func(s string) (bool, string, error) {
if s == "" {
return false, "", fmt.Errorf("should not be empty")
}
return true, "", nil
},
})

assert.Equal(t, text, "")
assert.NotNil(t, err)
})
})

Expand All @@ -105,11 +136,18 @@ func TestNonInteractiveUI(t *testing.T) {
})

t.Run("AskForChoice", func(t *testing.T) {
t.Run("panics", func(t *testing.T) {
t.Run("non negative default value", func(t *testing.T) {
parentUI := &fakeui.FakeUI{}
ui := NewNonInteractiveUI(parentUI)

assert.Panics(t, func() { ui.AskForChoice("", nil) })
choice, err := ui.AskForChoice(ChoiceOpts{
Label: "",
Default: 1,
Choices: []string{"a", "b", "c"},
})

assert.Equal(t, choice, 1)
assert.Nil(t, err)
})
})

Expand Down
8 changes: 4 additions & 4 deletions ui/non_tty_ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ func (ui *NonTTYUI) PrintTable(table Table) {
ui.parent.PrintTable(table)
}

func (ui *NonTTYUI) AskForText(label string) (string, error) {
return ui.parent.AskForText(label)
func (ui *NonTTYUI) AskForText(opts TextOpts) (string, error) {
return ui.parent.AskForText(opts)
}

func (ui *NonTTYUI) AskForChoice(label string, options []string) (int, error) {
return ui.parent.AskForChoice(label, options)
func (ui *NonTTYUI) AskForChoice(opts ChoiceOpts) (int, error) {
return ui.parent.AskForChoice(opts)
}

func (ui *NonTTYUI) AskForPassword(label string) (string, error) {
Expand Down
8 changes: 4 additions & 4 deletions ui/padding_ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,14 @@ func (ui *PaddingUI) PrintTable(table Table) {
ui.parent.PrintTable(table)
}

func (ui *PaddingUI) AskForText(label string) (string, error) {
func (ui *PaddingUI) AskForText(opts TextOpts) (string, error) {
ui.padBefore(paddingUIModeAskText)
return ui.parent.AskForText(label)
return ui.parent.AskForText(opts)
}

func (ui *PaddingUI) AskForChoice(label string, options []string) (int, error) {
func (ui *PaddingUI) AskForChoice(opts ChoiceOpts) (int, error) {
ui.padBefore(paddingUIModeAuto)
return ui.parent.AskForChoice(label, options)
return ui.parent.AskForChoice(opts)
}

func (ui *PaddingUI) AskForPassword(label string) (string, error) {
Expand Down
16 changes: 16 additions & 0 deletions ui/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package ui

// TextOpts Asking for text options
type TextOpts struct {
Label string
Default string
// ValidateFunc: method to validate input/default value
ValidateFunc func(string) (bool, string, error)
}

// ChoiceOpts asking for choice options
type ChoiceOpts struct {
Label string
Default int
Choices []string
}
41 changes: 29 additions & 12 deletions ui/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,27 +96,44 @@ func (ui *WriterUI) PrintTable(table Table) {
}
}

func (ui *WriterUI) AskForText(label string) (string, error) {
var text string

err := interact.NewInteraction(label).Resolve(&text)
if err != nil {
return "", fmt.Errorf("Asking for text: %s", err)
func (ui *WriterUI) AskForText(opts TextOpts) (string, error) {
if opts.ValidateFunc == nil {
opts.ValidateFunc = func(s string) (bool, string, error) {
return true, "", nil
}
}

return text, nil
for {
text := opts.Default
err := interact.NewInteraction(opts.Label).Resolve(&text)
if err != nil {
return "", fmt.Errorf("Asking for text: %s", err)
}

isValid, message, err := opts.ValidateFunc(text)
if err != nil {
return "", fmt.Errorf("Validation input: %s", err)
}
if isValid {
return text, nil
} else {
if len(message) == 0 {
message = "(reason for failure not specified)"
}
ui.ErrorLinef("Failed validation: %s", message)
}
}
}

func (ui *WriterUI) AskForChoice(label string, options []string) (int, error) {
func (ui *WriterUI) AskForChoice(opts ChoiceOpts) (int, error) {
var choices []interact.Choice

for i, opt := range options {
for i, opt := range opts.Choices {
choices = append(choices, interact.Choice{Display: opt, Value: i})
}

var chosen int

err := interact.NewInteraction(label, choices...).Resolve(&chosen)
chosen := opts.Default
err := interact.NewInteraction(opts.Label, choices...).Resolve(&chosen)
if err != nil {
return 0, fmt.Errorf("Asking for choice: %s", err)
}
Expand Down