/
cli.go
151 lines (125 loc) · 3.73 KB
/
cli.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package ui
import (
"bufio"
"errors"
"fmt"
"io"
"os"
"os/signal"
"strings"
"github.com/bgentry/speakeasy"
"github.com/fatih/color"
"github.com/mattn/go-isatty"
)
// CliHandler is the default handler and will write to stdout / stderr
type CliHandler struct {
Reader io.Reader
OutputWriter io.Writer
LogWriter io.Writer
previousLine string
}
// Ask user to input
func (hnd *CliHandler) Ask(query string) (string, error) {
return hnd.ask(query, false)
}
// AskSecret as for secret input
func (hnd *CliHandler) AskSecret(query string) (string, error) {
return hnd.ask(query, true)
}
// Debug prints a debug message
func (hnd *CliHandler) Debug(msg string, args ...interface{}) {
hnd.printLine(hnd.LogWriter, msg, args...)
}
// Info prints message to standard out
func (hnd *CliHandler) Info(msg string, args ...interface{}) {
hnd.printLine(hnd.LogWriter, msg, args...)
}
// Warn prints message
func (hnd *CliHandler) Warn(msg string, args ...interface{}) {
hnd.printLine(hnd.LogWriter, color.YellowString(msg), args...)
}
// Error prints message
func (hnd *CliHandler) Error(msg string, args ...interface{}) {
hnd.printLine(hnd.LogWriter, msg, args...)
}
// Fatal prints message and makes sure to fail
func (hnd *CliHandler) Fatal(msg string, args ...interface{}) {
hnd.printLine(hnd.LogWriter, color.RedString(msg), args...)
os.Exit(1)
}
// Output prints to output writer
func (hnd *CliHandler) Output(msg string, args ...interface{}) {
hnd.printLine(hnd.OutputWriter, msg, args...)
}
// Header does nothing
func (hnd *CliHandler) Header(msg string) {
hnd.NewLine()
hnd.printLine(hnd.LogWriter, color.New(color.Bold).Sprint(msg))
}
// Separator does nothing
func (hnd *CliHandler) Separator(title string) {
hnd.printLine(hnd.LogWriter, "")
hnd.printLine(hnd.LogWriter, "------------------------------------------------------------------------")
if title != "" {
hnd.printLine(hnd.LogWriter, color.New(color.Bold).Sprint(title))
hnd.printLine(hnd.LogWriter, "------------------------------------------------------------------------")
}
hnd.printLine(hnd.LogWriter, "")
}
// NewLine creates a new line, except if previous line was a new line.
// Do not want 2 new lines after another
func (hnd *CliHandler) NewLine() {
hnd.printLine(hnd.LogWriter, "")
}
// printLine writes a line to writer and saves it in temporary variable. It will
// never print 2 empty lines after another.
func (hnd *CliHandler) printLine(writer io.Writer, msg string, args ...interface{}) {
if hnd.previousLine == "" && msg == "" {
return
}
line := fmt.Sprintf(msg, args...)
hnd.previousLine = line
fmt.Fprint(writer, line)
fmt.Fprintln(writer)
}
// ask for user input
// implementation based on https://github.com/mitchellh/cli/blob/master/ui.go
func (hnd *CliHandler) ask(query string, secret bool) (string, error) {
if _, err := fmt.Fprint(os.Stdout, query+" "); err != nil {
return "", err
}
// Register for interrupts so that we can catch it and immediately
// return...
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt)
defer signal.Stop(sigCh)
// Ask for input in a go-routine so that we can ignore it.
errCh := make(chan error, 1)
lineCh := make(chan string, 1)
go func() {
var line string
var err error
if secret && isatty.IsTerminal(os.Stdin.Fd()) {
line, err = speakeasy.Ask("")
} else {
r := bufio.NewReader(hnd.Reader)
line, err = r.ReadString('\n')
}
if err != nil {
errCh <- err
return
}
lineCh <- strings.TrimRight(line, "\r\n")
}()
select {
case err := <-errCh:
return "", err
case line := <-lineCh:
return line, nil
case <-sigCh:
// Print a newline so that any further output starts properly
// on a new line.
fmt.Fprintln(hnd.LogWriter)
return "", errors.New("interrupted")
}
}