Issues (5)

msg/msg.go (2 issues)

Severity
1
package msg
2
3
import (
4
	"bufio"
5
	"fmt"
6
	"io"
7
	"os"
8
	"strings"
9
	"sync"
10
11
	"github.com/Masterminds/vcs"
12
)
13
14
// Messenger provides the underlying implementation that displays output to
15
// users.
16
type Messenger struct {
17
	sync.Mutex
18
19
	// Quiet, if true, suppresses chatty levels, like Info.
20
	Quiet bool
21
22
	// IsDebugging, if true, shows Debug.
23
	IsDebugging bool
24
25
	// NoColor, if true, will not use color in the output.
26
	NoColor bool
27
28
	// Stdout is the location where this prints output.
29
	Stdout io.Writer
30
31
	// Stderr is the location where this prints logs.
32
	Stderr io.Writer
33
34
	// Stdin is the location where input is read.
35
	Stdin io.Reader
36
37
	// PanicOnDie if true Die() will panic instead of exiting.
38
	PanicOnDie bool
39
40
	// The default exit code to use when dyping
41
	ecode int
42
43
	// If an error was been sent.
44
	hasErrored bool
45
}
46
47
// NewMessenger creates a default Messenger to display output.
48
func NewMessenger() *Messenger {
49
	m := &Messenger{
50
		Quiet:       false,
51
		IsDebugging: false,
52
		NoColor:     false,
53
		Stdout:      os.Stdout,
54
		Stderr:      os.Stderr,
55
		Stdin:       os.Stdin,
56
		PanicOnDie:  false,
57
		ecode:       1,
58
	}
59
60
	return m
61
}
62
63
// Default contains a default Messenger used by package level functions
64
var Default = NewMessenger()
65
66
// Info logs information
67
func (m *Messenger) Info(msg string, args ...interface{}) {
68
	if m.Quiet {
69
		return
70
	}
71
	prefix := m.Color(Green, "[INFO]\t")
72
	m.Msg(prefix+msg, args...)
73
}
74
75
// Info logs information using the Default Messenger
76
func Info(msg string, args ...interface{}) {
77
	Default.Info(msg, args...)
78
}
79
80
// Debug logs debug information
81
func (m *Messenger) Debug(msg string, args ...interface{}) {
82
	if m.Quiet || !m.IsDebugging {
83
		return
84
	}
85
	prefix := "[DEBUG]\t"
86
	m.Msg(prefix+msg, args...)
87
}
88
89
// Debug logs debug information using the Default Messenger
90
func Debug(msg string, args ...interface{}) {
91
	Default.Debug(msg, args...)
92
}
93
94
// Warn logs a warning
95
func (m *Messenger) Warn(msg string, args ...interface{}) {
96
	prefix := m.Color(Yellow, "[WARN]\t")
97
	m.Msg(prefix+msg, args...)
98
}
99
100
// Warn logs a warning using the Default Messenger
101
func Warn(msg string, args ...interface{}) {
102
	Default.Warn(msg, args...)
103
}
104
105
// Err logs an error.
106
func (m *Messenger) Err(msg string, args ...interface{}) {
107
	prefix := m.Color(Red, "[ERROR]\t")
108
	m.Msg(prefix+msg, args...)
109
	m.hasErrored = true
110
}
111
112
// Err logs anderror using the Default Messenger
113
func Err(msg string, args ...interface{}) {
114
	Default.Err(msg, args...)
115
}
116
117
// Die prints an error message and immediately exits the application.
118
// If PanicOnDie is set to true a panic will occur instead of os.Exit being
119
// called.
120
func (m *Messenger) Die(msg string, args ...interface{}) {
121
	m.Err(msg, args...)
122
	if m.PanicOnDie {
123
		panic("trapped a Die() call")
124
	}
125
	os.Exit(m.ecode)
126
}
127
128
// Die prints an error message and immediately exits the application using the
129
// Default Messenger. If PanicOnDie is set to true a panic will occur instead of
130
// os.Exit being called.
131
func Die(msg string, args ...interface{}) {
132
	Default.Die(msg, args...)
133
}
134
135
// ExitCode sets the exit code used by Die.
136
//
137
// The default is 1.
138
//
139
// Returns the old error code.
140
func (m *Messenger) ExitCode(e int) int {
141
	m.Lock()
142
	old := m.ecode
143
	m.ecode = e
144
	m.Unlock()
145
	return old
146
}
147
148
// ExitCode sets the exit code used by Die using the Default Messenger.
149
//
150
// The default is 1.
151
//
152
// Returns the old error code.
153
func ExitCode(e int) int {
154
	return Default.ExitCode(e)
155
}
156
157
// Msg prints a message with optional arguments, that can be printed, of
158
// varying types.
159
func (m *Messenger) Msg(msg string, args ...interface{}) {
160
	// When operations in Glide are happening concurrently messaging needs to be
161
	// locked to avoid displaying one message in the middle of another one.
162
	m.Lock()
163
	defer m.Unlock()
164
	// Get rid of the annoying fact that messages need \n at the end, but do
165
	// it in a backward compatible way.
166
	if !strings.HasSuffix(msg, "\n") {
167
		msg += "\n"
168
	}
169
170
	if len(args) == 0 {
171
		fmt.Fprint(m.Stderr, msg)
172
	} else {
173
		fmt.Fprintf(m.Stderr, msg, args...)
0 ignored issues
show
can't check non-constant format in call to Fprintf
Loading history...
174
	}
175
176
	// If an arg is a vcs error print the output if in debug mode. This is
177
	// capured here rather than calling Debug because concurrent operations
178
	// could cause other messages to appear between the initial error and the
179
	// debug output by unlocking and calling Debug.
180
	if len(args) != 0 && !m.Quiet && m.IsDebugging {
181
		if err, ok := args[len(args)-1].(error); ok {
182
			switch t := err.(type) {
183
			case *vcs.LocalError:
184
				fmt.Fprintf(m.Stderr, "[DEBUG]\tOutput was: %s", strings.TrimSpace(t.Out()))
185
			case *vcs.RemoteError:
186
				fmt.Fprintf(m.Stderr, "[DEBUG]\tOutput was: %s", strings.TrimSpace(t.Out()))
187
			}
188
		}
189
	}
190
}
191
192
// Msg prints a message with optional arguments, that can be printed, of
193
// varying types using the Default Messenger.
194
func Msg(msg string, args ...interface{}) {
195
	Default.Msg(msg, args...)
196
}
197
198
// Puts formats a message and then prints to Stdout.
199
//
200
// It does not prefix the message, does not color it, or otherwise decorate it.
201
//
202
// It does add a line feed.
203
func (m *Messenger) Puts(msg string, args ...interface{}) {
204
	// When operations in Glide are happening concurrently messaging needs to be
205
	// locked to avoid displaying one message in the middle of another one.
206
	m.Lock()
207
	defer m.Unlock()
208
209
	fmt.Fprintf(m.Stdout, msg, args...)
0 ignored issues
show
can't check non-constant format in call to Fprintf
Loading history...
210
	fmt.Fprintln(m.Stdout)
211
}
212
213
// Puts formats a message and then prints to Stdout using the Default Messenger.
214
//
215
// It does not prefix the message, does not color it, or otherwise decorate it.
216
//
217
// It does add a line feed.
218
func Puts(msg string, args ...interface{}) {
219
	Default.Puts(msg, args...)
220
}
221
222
// Print prints exactly the string given.
223
//
224
// It prints to Stdout.
225
func (m *Messenger) Print(msg string) {
226
	// When operations in Glide are happening concurrently messaging needs to be
227
	// locked to avoid displaying one message in the middle of another one.
228
	m.Lock()
229
	defer m.Unlock()
230
231
	fmt.Fprint(m.Stdout, msg)
232
}
233
234
// Print prints exactly the string given using the Default Messenger.
235
//
236
// It prints to Stdout.
237
func Print(msg string) {
238
	Default.Print(msg)
239
}
240
241
// HasErrored returns if Error has been called.
242
//
243
// This is useful if you want to known if Error was called to exit with a
244
// non-zero exit code.
245
func (m *Messenger) HasErrored() bool {
246
	return m.hasErrored
247
}
248
249
// HasErrored returns if Error has been called on the Default Messenger.
250
//
251
// This is useful if you want to known if Error was called to exit with a
252
// non-zero exit code.
253
func HasErrored() bool {
254
	return Default.HasErrored()
255
}
256
257
// Color returns a string in a certain color if colors are enabled and
258
// available on that platform.
259
func Color(code, msg string) string {
260
	return Default.Color(code, msg)
261
}
262
263
// PromptUntil provides a prompt until one of the passed in strings has been
264
// entered and return is hit. Note, the comparisons are case insensitive meaning
265
// Y == y. The returned value is the one from the passed in options (same case).
266
func (m *Messenger) PromptUntil(opts []string) (string, error) {
267
	reader := bufio.NewReader(os.Stdin)
268
	for {
269
		text, err := reader.ReadString('\n')
270
		if err != nil {
271
			return "", err
272
		}
273
274
		for _, c := range opts {
275
			if strings.EqualFold(c, strings.TrimSpace(text)) {
276
				return c, nil
277
			}
278
		}
279
	}
280
}
281
282
// PromptUntil provides a prompt until one of the passed in strings has been
283
// entered and return is hit. Note, the comparisons are case insensitive meaning
284
// Y == y. The returned value is the one from the passed in options (same case).
285
// Uses the default setup.
286
func PromptUntil(opts []string) (string, error) {
287
	return Default.PromptUntil(opts)
288
}
289
290
// PromptUntilYorN provides a prompt until the user chooses yes or no. This is
291
// not case sensitive and they can input other options such as Y or N.
292
// In the response Yes is bool true and No is bool false.
293
func (m *Messenger) PromptUntilYorN() bool {
294
	res, err := m.PromptUntil([]string{"y", "yes", "n", "no"})
295
	if err != nil {
296
		m.Die("Error processing response: %s", err)
297
	}
298
299
	if res == "y" || res == "yes" {
300
		return true
301
	}
302
303
	return false
304
}
305
306
// PromptUntilYorN provides a prompt until the user chooses yes or no. This is
307
// not case sensitive and they can input other options such as Y or N.
308
// In the response Yes is bool true and No is bool false.
309
// Uses the default setup.
310
func PromptUntilYorN() bool {
311
	return Default.PromptUntilYorN()
312
}
313