package pterm import ( "fmt" "runtime" "strings" "github.com/pterm/pterm/internal" ) var ( // GrayBoxStyle wraps text in a gray box. GrayBoxStyle = NewStyle(BgGray, FgLightWhite) ) var ( // Info returns a PrefixPrinter, which can be used to print text with an "info" Prefix. Info = PrefixPrinter{ MessageStyle: &ThemeDefault.InfoMessageStyle, Prefix: Prefix{ Style: &ThemeDefault.InfoPrefixStyle, Text: "INFO", }, } // Warning returns a PrefixPrinter, which can be used to print text with a "warning" Prefix. Warning = PrefixPrinter{ MessageStyle: &ThemeDefault.WarningMessageStyle, Prefix: Prefix{ Style: &ThemeDefault.WarningPrefixStyle, Text: "WARNING", }, } // Success returns a PrefixPrinter, which can be used to print text with a "success" Prefix. Success = PrefixPrinter{ MessageStyle: &ThemeDefault.SuccessMessageStyle, Prefix: Prefix{ Style: &ThemeDefault.SuccessPrefixStyle, Text: "SUCCESS", }, } // Error returns a PrefixPrinter, which can be used to print text with an "error" Prefix. Error = PrefixPrinter{ MessageStyle: &ThemeDefault.ErrorMessageStyle, Prefix: Prefix{ Style: &ThemeDefault.ErrorPrefixStyle, Text: " ERROR ", }, } // Fatal returns a PrefixPrinter, which can be used to print text with an "fatal" Prefix. // NOTICE: Fatal terminates the application immediately! Fatal = PrefixPrinter{ MessageStyle: &ThemeDefault.FatalMessageStyle, Prefix: Prefix{ Style: &ThemeDefault.FatalPrefixStyle, Text: " FATAL ", }, Fatal: true, } // Debug Prints debug messages. By default it will only print if PrintDebugMessages is true. // You can change PrintDebugMessages with EnableDebugMessages and DisableDebugMessages, or by setting the variable itself. Debug = PrefixPrinter{ MessageStyle: &ThemeDefault.DebugMessageStyle, Prefix: Prefix{ Text: " DEBUG ", Style: &ThemeDefault.DebugPrefixStyle, }, Debugger: true, } // Description returns a PrefixPrinter, which can be used to print text with a "description" Prefix. Description = PrefixPrinter{ MessageStyle: &ThemeDefault.DescriptionMessageStyle, Prefix: Prefix{ Style: &ThemeDefault.DescriptionPrefixStyle, Text: "Description", }, } ) // PrefixPrinter is the printer used to print a Prefix. type PrefixPrinter struct { Prefix Prefix Scope Scope MessageStyle *Style Fatal bool ShowLineNumber bool LineNumberOffset int // If Debugger is true, the printer will only print if PrintDebugMessages is set to true. // You can change PrintDebugMessages with EnableDebugMessages and DisableDebugMessages, or by setting the variable itself. Debugger bool } // WithPrefix adds a custom prefix to the printer. func (p PrefixPrinter) WithPrefix(prefix Prefix) *PrefixPrinter { p.Prefix = prefix return &p } // WithScope adds a scope to the Prefix. func (p PrefixPrinter) WithScope(scope Scope) *PrefixPrinter { p.Scope = scope return &p } // WithMessageStyle adds a custom prefix to the printer. func (p PrefixPrinter) WithMessageStyle(style *Style) *PrefixPrinter { p.MessageStyle = style return &p } // WithFatal sets if the printer should panic after printing. // NOTE: // The printer will only panic if either PrefixPrinter.Println, PrefixPrinter.Print // or PrefixPrinter.Printf is called. func (p PrefixPrinter) WithFatal(b ...bool) *PrefixPrinter { p.Fatal = internal.WithBoolean(b) return &p } // WithShowLineNumber sets if the printer should print the line number from where it's called in a go file. func (p PrefixPrinter) WithShowLineNumber(b ...bool) *PrefixPrinter { p.ShowLineNumber = internal.WithBoolean(b) return &p } // WithDebugger returns a new Printer with specific Debugger value. // If Debugger is true, the printer will only print if PrintDebugMessages is set to true. // You can change PrintDebugMessages with EnableDebugMessages and DisableDebugMessages, or by setting the variable itself. func (p PrefixPrinter) WithDebugger(b ...bool) *PrefixPrinter { p.Debugger = internal.WithBoolean(b) return &p } // WithLineNumberOffset can be used to exclude a specific amount of calls in the call stack. // If you make a wrapper function for example, you can set this to one. // The printed line number will then be the line number where your wrapper function is called. func (p PrefixPrinter) WithLineNumberOffset(offset int) *PrefixPrinter { p.LineNumberOffset = offset return &p } // Sprint formats using the default formats for its operands and returns the resulting string. // Spaces are added between operands when neither is a string. func (p *PrefixPrinter) Sprint(a ...interface{}) string { m := Sprint(a...) if p.Debugger && !PrintDebugMessages { return "" } if RawOutput { if p.Prefix.Text != "" { return Sprintf("%s: %s", strings.TrimSpace(p.Prefix.Text), Sprint(a...)) } else { return Sprint(a...) } } if p.Prefix.Style == nil { p.Prefix.Style = NewStyle() } if p.Scope.Style == nil { p.Scope.Style = NewStyle() } if p.MessageStyle == nil { p.MessageStyle = NewStyle() } var ret string var newLine bool if strings.HasSuffix(m, "\n") { m = strings.TrimRight(m, "\n") newLine = true } messageLines := strings.Split(m, "\n") for i, m := range messageLines { if i == 0 { ret += p.GetFormattedPrefix() + " " if p.Scope.Text != "" { ret += NewStyle(*p.Scope.Style...).Sprint(" (" + p.Scope.Text + ") ") } ret += p.MessageStyle.Sprint(m) } else { ret += "\n" + p.Prefix.Style.Sprint(strings.Repeat(" ", len(p.Prefix.Text)+2)) + " " + p.MessageStyle.Sprint(m) } } _, fileName, line, _ := runtime.Caller(3 + p.LineNumberOffset) if p.ShowLineNumber { ret += FgGray.Sprint("\n└ " + fmt.Sprintf("(%s:%d)\n", fileName, line)) newLine = false } if newLine { ret += "\n" } return Sprint(ret) } // Sprintln formats using the default formats for its operands and returns the resulting string. // Spaces are always added between operands and a newline is appended. func (p PrefixPrinter) Sprintln(a ...interface{}) string { if p.Debugger && !PrintDebugMessages { return "" } str := fmt.Sprintln(a...) return p.Sprint(str) } // Sprintf formats according to a format specifier and returns the resulting string. func (p PrefixPrinter) Sprintf(format string, a ...interface{}) string { if p.Debugger && !PrintDebugMessages { return "" } return p.Sprint(Sprintf(format, a...)) } // Sprintfln formats according to a format specifier and returns the resulting string. // Spaces are always added between operands and a newline is appended. func (p PrefixPrinter) Sprintfln(format string, a ...interface{}) string { if p.Debugger && !PrintDebugMessages { return "" } return p.Sprintf(format, a...) + "\n" } // Print formats using the default formats for its operands and writes to standard output. // Spaces are added between operands when neither is a string. // It returns the number of bytes written and any write error encountered. func (p *PrefixPrinter) Print(a ...interface{}) *TextPrinter { tp := TextPrinter(p) if p.Debugger && !PrintDebugMessages { return &tp } Print(p.Sprint(a...)) checkFatal(p) return &tp } // Println formats using the default formats for its operands and writes to standard output. // Spaces are always added between operands and a newline is appended. // It returns the number of bytes written and any write error encountered. func (p *PrefixPrinter) Println(a ...interface{}) *TextPrinter { tp := TextPrinter(p) if p.Debugger && !PrintDebugMessages { return &tp } Print(p.Sprintln(a...)) checkFatal(p) return &tp } // Printf formats according to a format specifier and writes to standard output. // It returns the number of bytes written and any write error encountered. func (p *PrefixPrinter) Printf(format string, a ...interface{}) *TextPrinter { tp := TextPrinter(p) if p.Debugger && !PrintDebugMessages { return &tp } Print(p.Sprintf(format, a...)) checkFatal(p) return &tp } // Printfln formats according to a format specifier and writes to standard output. // Spaces are always added between operands and a newline is appended. // It returns the number of bytes written and any write error encountered. func (p *PrefixPrinter) Printfln(format string, a ...interface{}) *TextPrinter { tp := TextPrinter(p) if p.Debugger && !PrintDebugMessages { return &tp } Print(p.Sprintfln(format, a...)) checkFatal(p) return &tp } // PrintOnError prints every error which is not nil. // If every error is nil, nothing will be printed. // This can be used for simple error checking. // // Note: Use WithFatal(true) or Fatal to panic after first non nil error. func (p *PrefixPrinter) PrintOnError(a ...interface{}) *TextPrinter { for _, arg := range a { if err, ok := arg.(error); ok { if err != nil { p.Println(err) } } } tp := TextPrinter(p) return &tp } // PrintOnErrorf wraps every error which is not nil and prints it. // If every error is nil, nothing will be printed. // This can be used for simple error checking. func (p *PrefixPrinter) PrintOnErrorf(format string, a ...interface{}) *TextPrinter { for _, arg := range a { if err, ok := arg.(error); ok { if err != nil { p.Println(fmt.Errorf(format, err)) } } } tp := TextPrinter(p) return &tp } // GetFormattedPrefix returns the Prefix as a styled text string. func (p PrefixPrinter) GetFormattedPrefix() string { return p.Prefix.Style.Sprint(" " + p.Prefix.Text + " ") } // Prefix contains the data used as the beginning of a printed text via a PrefixPrinter. type Prefix struct { Text string Style *Style } // Scope contains the data of the optional scope of a prefix. // If it has a text, it will be printed after the Prefix in brackets. type Scope struct { Text string Style *Style } func checkFatal(p *PrefixPrinter) { if p.Fatal { panic("") } }