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  }