summaryrefslogtreecommitdiff
path: root/internal/cli/interface.go
diff options
context:
space:
mode:
authorStefan Majewsky <majewsky@gmx.net>2020-03-28 13:53:10 +0100
committerStefan Majewsky <majewsky@gmx.net>2020-03-28 13:53:10 +0100
commited2b9374599cc00467b1c27121318748cc0d3f49 (patch)
tree65e3200a0b69817b24ae772223d1d852a52458af /internal/cli/interface.go
parent1ff86053ddef8de3247909434e0bd7000c060f2a (diff)
downloadgofu-ed2b9374599cc00467b1c27121318748cc0d3f49.tar.gz
rename pkg/ to internal/
Diffstat (limited to 'internal/cli/interface.go')
-rw-r--r--internal/cli/interface.go168
1 files changed, 168 insertions, 0 deletions
diff --git a/internal/cli/interface.go b/internal/cli/interface.go
new file mode 100644
index 0000000..a88212b
--- /dev/null
+++ b/internal/cli/interface.go
@@ -0,0 +1,168 @@
+/*******************************************************************************
+*
+* Copyright 2017 Stefan Majewsky <majewsky@gmx.net>
+*
+* This program is free software: you can redistribute it and/or modify it under
+* the terms of the GNU General Public License as published by the Free Software
+* Foundation, either version 3 of the License, or (at your option) any later
+* version.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT ANY
+* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License along with
+* this program. If not, see <http://www.gnu.org/licenses/>.
+*
+*******************************************************************************/
+
+package cli
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ "os"
+ "sort"
+ "strings"
+
+ "golang.org/x/crypto/ssh/terminal"
+)
+
+//Interface wraps access to the CLI, including input, output and subprocesses.
+var Interface *Implementation
+
+func init() {
+ SetupInterface(os.Stdin, os.Stdout, os.Stderr, DefaultCommandRunner)
+}
+
+//SetupInterface prepares the Interface instance with nonstandard file streams
+//or a nonstandard CommandRunner. This is only required for unit tests.
+func SetupInterface(stdin io.Reader, stdout, stderr io.Writer, commandRunner CommandRunner) {
+ Interface = &Implementation{
+ stdin: stdin,
+ stdout: stdout,
+ stderr: stderr,
+ stdinBuf: bufio.NewReader(stdin),
+ commandRunner: commandRunner,
+ }
+
+ if stdinFile, ok := stdin.(*os.File); ok && terminal.IsTerminal(int(stdinFile.Fd())) {
+ Interface.tui = &terminalTUI{Interface}
+ } else {
+ Interface.tui = &pipeTUI{Interface}
+ }
+}
+
+//Implementation wraps access to the CLI, including input, output and subprocesses.
+type Implementation struct {
+ stdin io.Reader
+ stdout io.Writer
+ stderr io.Writer
+ stdinBuf *bufio.Reader
+ tui TUI
+ commandRunner CommandRunner
+ //If this flag is set, only ShowResult() will write into stdout; everything
+ //else that usually goes to stdout goes to stderr instead.
+ //
+ //This is useful when gofu is expected to output a certain value to stdout
+ //which is used by the next program in the pipe, and additional output from
+ //subprocesses could confuse the stdout handler.
+ StdoutProtected bool
+}
+
+//TUI provides the interactive parts of the cli.Implementation, so that these can be
+//easily swapped out for mock implementations in unit tests.
+type TUI interface {
+ //ReadLine reads a line from stdin (if tty: uses canonical mode).
+ ReadLine(prompt string) (string, error)
+ //Confirm displays a yes/no question and returns whether the user answered "yes".
+ Confirm(question string) (bool, error)
+ //Query displays a question and a set of answers and allows the user to select
+ //one of the answers. Returns the Return attribute of the selected Choice.
+ Query(prompt string, choices ...Choice) (string, error)
+ //Print writes the given string (potentially including ANSI escape codes) to
+ //the given writer. At this point, it can be decided whether to strip out the
+ //ANSI escape codes.
+ Print(w io.Writer, msg string)
+}
+
+func (i *Implementation) safeStdout() io.Writer {
+ if i.StdoutProtected {
+ return i.stderr
+ }
+ return i.stdout
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// input
+
+//ReadLine reads a line from stdin (if tty: uses canonical mode).
+func (i *Implementation) ReadLine(prompt string) (string, error) {
+ return i.tui.ReadLine(prompt)
+}
+
+//Confirm displays a yes/no question and returns whether the user answered "yes".
+func (i *Implementation) Confirm(question string) (bool, error) {
+ return i.tui.Confirm(question)
+}
+
+//Query displays a question and a set of answers and allows the user to select
+//one of the answers. Returns the Return attribute of the selected Choice.
+func (i *Implementation) Query(prompt string, choices ...Choice) (string, error) {
+ return i.tui.Query(prompt, choices...)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// subprocesses
+
+//Run executes the given command on the same stdout and stderr.
+func (i *Implementation) Run(c Command) error {
+ return i.commandRunner(c, nil, i.safeStdout(), i.stderr)
+}
+
+//CaptureStdout executes the given command on the same stderr and captures its stdout.
+func (i *Implementation) CaptureStdout(c Command) (string, error) {
+ var buf bytes.Buffer
+ err := i.commandRunner(c, nil, &buf, i.stderr)
+ return string(buf.Bytes()), err
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// output
+
+//ShowResult displays the result of a computation on stdout.
+func (i *Implementation) ShowResult(str string) {
+ str = strings.TrimSpace(str) + "\n"
+ i.stdout.Write([]byte(str))
+}
+
+//ShowResultsSorted calls ShowResult() on each of the results after sorting them.
+func (i *Implementation) ShowResultsSorted(strs []string) {
+ sort.Strings(strs)
+ for _, str := range strs {
+ i.ShowResult(str)
+ }
+}
+
+//ShowProgress displays a progress message on stderr.
+func (i *Implementation) ShowProgress(str string) {
+ i.tui.Print(i.stderr, fmt.Sprintf("\x1B[0;1;36m>>\x1B[0;36m %s\x1B[0m\n", strings.TrimSpace(str)))
+}
+
+//ShowWarning displays a warning message on stderr.
+func (i *Implementation) ShowWarning(str string) {
+ i.tui.Print(i.stderr, fmt.Sprintf("\x1B[0;1;33m!!\x1B[0;33m %s\x1B[0m\n", strings.TrimSpace(str)))
+}
+
+//ShowError displays an error message on stderr.
+func (i *Implementation) ShowError(str string) {
+ i.tui.Print(i.stderr, fmt.Sprintf("\x1B[0;1;31m!!\x1B[0;31m %s\x1B[0m\n", strings.TrimSpace(str)))
+}
+
+//ShowUsage displays a usage synopsis on stderr.
+func (i *Implementation) ShowUsage(str string) {
+ str = strings.TrimSpace(str) + "\n"
+ i.stderr.Write([]byte(str))
+}