github.com/soltysh/glide@v0.13.2/msg/msg.go (about) 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...) 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...) 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 }