github.com/clamoriniere/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  }