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