summaryrefslogtreecommitdiff
path: root/pkg/cli
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/cli')
-rw-r--r--pkg/cli/interface.go18
-rw-r--r--pkg/cli/query.go62
2 files changed, 75 insertions, 5 deletions
diff --git a/pkg/cli/interface.go b/pkg/cli/interface.go
index 3192d32..c188c77 100644
--- a/pkg/cli/interface.go
+++ b/pkg/cli/interface.go
@@ -26,18 +26,26 @@ import (
"os"
"sort"
"strings"
+
+ "golang.org/x/crypto/ssh/terminal"
)
//NewInterface creates an Interface instance.
-func NewInterface(stdin, stdout, stderr *os.File) (*Interface, error) {
- //TODO: check terminal.IsTerminal(int(stdin.Fd())) and choose the TUI instance accordingly
- return &Interface{
+func NewInterface(stdin, stdout, stderr *os.File) *Interface {
+ i := &Interface{
stdin: stdin,
stdout: stdout,
stderr: stderr,
stdinBuf: bufio.NewReader(stdin),
- tui: &terminalTUI{},
- }, nil
+ }
+
+ if terminal.IsTerminal(int(stdin.Fd())) {
+ i.tui = &terminalTUI{i}
+ } else {
+ i.tui = &pipeTUI{i}
+ }
+
+ return i
}
//Interface wraps access to the CLI, including input, output and subprocesses.
diff --git a/pkg/cli/query.go b/pkg/cli/query.go
index 2a6a5bc..ed9e913 100644
--- a/pkg/cli/query.go
+++ b/pkg/cli/query.go
@@ -19,9 +19,11 @@
package cli
import (
+ "errors"
"fmt"
"io"
"regexp"
+ "strconv"
"strings"
terminal "golang.org/x/crypto/ssh/terminal"
@@ -35,6 +37,9 @@ func (e errInterrupted) Error() string {
return "Interrupted!"
}
+////////////////////////////////////////////////////////////////////////////////
+// TUI implementation for when stdin is a terminal
+
type terminalTUI struct {
i *Interface
}
@@ -218,3 +223,60 @@ func (b *buffer) getNextInput() []byte {
return b.getNextInput() //restart
}
+
+////////////////////////////////////////////////////////////////////////////////
+// TUI implementation for when stdin is a pipe
+
+type pipeTUI struct {
+ i *Interface
+}
+
+func (t *pipeTUI) ReadLine(prompt string) (string, error) {
+ str, err := t.i.stdinBuf.ReadString('\n')
+ str = strings.TrimSpace(str)
+ if err == nil {
+ fmt.Fprintf(t.i.stderr, "%s %s\n", strings.TrimSpace(prompt), str)
+ }
+ return str, err
+}
+
+func (t *pipeTUI) Confirm(question string) (bool, error) {
+ question = strings.TrimSpace(question)
+ str, err := t.ReadLine(question)
+ if err != nil {
+ return false, err
+ }
+ //recognize /[01tTfF]|true|false/
+ ok, err := strconv.ParseBool(str)
+ if err != nil {
+ //recognize /[yY]|yes|no/
+ ok = strings.HasPrefix(question, "y") || strings.HasPrefix(question, "Y")
+ }
+ fmt.Fprintf(t.i.stderr, "%s -> %v (%s)\n", question, ok, str)
+ return ok, nil
+}
+
+//Query for the pipe TUI is limited to exact matches of the choice text, or
+//matching by choice shortcut.
+func (t *pipeTUI) Query(prompt string, choices ...Choice) (string, error) {
+ str, err := t.i.stdinBuf.ReadString('\n')
+ if err != nil {
+ return str, err
+ }
+ //prefer exact match on choice.Text
+ for _, choice := range choices {
+ if choice.Text == str {
+ fmt.Fprintf(t.i.stderr, "%s -> %s\n", prompt, choice.Text)
+ return choice.Return, nil
+ }
+ }
+ //allow match on choice.Shortcut
+ for _, choice := range choices {
+ if string(choice.Shortcut) == str {
+ fmt.Fprintf(t.i.stderr, "%s -> %s\n", prompt, choice.Text)
+ return choice.Return, nil
+ }
+ }
+ fmt.Fprintf(t.i.stderr, "%s -> [%s]\n", prompt, str)
+ return "", errors.New("cannot match input with available choices")
+}