2020-08-06 20:58:47 +03:00
|
|
|
package output
|
|
|
|
|
2023-05-03 15:36:10 +03:00
|
|
|
import (
|
2023-08-12 21:54:14 +03:00
|
|
|
"log/slog"
|
2023-05-07 05:36:34 +03:00
|
|
|
"time"
|
|
|
|
|
2023-11-20 04:06:36 +03:00
|
|
|
"github.com/fatih/color"
|
2023-05-22 18:08:14 +03:00
|
|
|
"github.com/samber/lo"
|
|
|
|
|
2023-05-07 05:36:34 +03:00
|
|
|
"github.com/neilotoole/sq/libsq/core/timez"
|
2023-05-03 15:36:10 +03:00
|
|
|
)
|
2020-08-06 20:58:47 +03:00
|
|
|
|
2023-04-22 06:36:32 +03:00
|
|
|
// Printing describes color and pretty-printing options.
|
|
|
|
type Printing struct {
|
2023-05-07 05:36:34 +03:00
|
|
|
// monochrome is controlled by EnableColor.
|
2020-08-06 20:58:47 +03:00
|
|
|
monochrome bool
|
|
|
|
|
2023-05-03 15:36:10 +03:00
|
|
|
// FlushThreshold is the size in bytes after which an output writer
|
|
|
|
// should flush any internal buffer.
|
|
|
|
FlushThreshold int
|
|
|
|
|
2022-12-17 06:46:37 +03:00
|
|
|
// ShowHeader indicates that a header (e.g. a header row) should
|
|
|
|
// be printed where applicable.
|
|
|
|
ShowHeader bool
|
|
|
|
|
|
|
|
// Verbose indicates that verbose output should be printed where
|
|
|
|
// applicable.
|
|
|
|
Verbose bool
|
|
|
|
|
2023-05-05 20:41:22 +03:00
|
|
|
// Compact indicates that output should not be pretty-printed.
|
2023-04-22 06:36:32 +03:00
|
|
|
// Typically this means indentation, new lines, etc., but
|
2020-08-06 20:58:47 +03:00
|
|
|
// varies by output format.
|
2023-05-05 20:41:22 +03:00
|
|
|
Compact bool
|
2020-08-06 20:58:47 +03:00
|
|
|
|
|
|
|
// Indent is the indent string to use when pretty-printing,
|
|
|
|
// typically two spaces.
|
|
|
|
Indent string
|
|
|
|
|
2023-04-16 01:28:51 +03:00
|
|
|
// Redact indicates that sensitive fields (such as passwords)
|
|
|
|
// should be redacted (hidden/masked).
|
|
|
|
//
|
|
|
|
// TODO: Redact is not being honored by the writers.
|
|
|
|
Redact bool
|
|
|
|
|
2023-05-07 05:36:34 +03:00
|
|
|
// FormatDatetime formats a timestamp e.g. 2020-11-12T13:14:15Z.
|
|
|
|
// Defaults to timez.DefaultDatetime.
|
|
|
|
FormatDatetime func(time time.Time) string
|
|
|
|
|
|
|
|
// FormatDatetimeAsNumber indicates that datetime values should be
|
|
|
|
// rendered as naked numbers (instead of as a string) if possible.
|
|
|
|
// See cli.OptDatetimeFormatAsNumber.
|
|
|
|
FormatDatetimeAsNumber bool
|
|
|
|
|
|
|
|
// FormatTime formats a time of day, e.g. 13:14:15.
|
|
|
|
// Defaults to timez.DefaultTime.
|
|
|
|
FormatTime func(time time.Time) string
|
|
|
|
|
|
|
|
// FormatTimeAsNumber indicates that time values should be
|
|
|
|
// rendered as naked numbers (instead of as a string) if possible.
|
|
|
|
// See cli.OptTimeFormatAsNumber.
|
|
|
|
FormatTimeAsNumber bool
|
|
|
|
|
|
|
|
// FormatDate formats a date, e.g. 2020-11-12.
|
|
|
|
// Defaults to timez.DefaultDate.
|
|
|
|
FormatDate func(time time.Time) string
|
|
|
|
|
|
|
|
// FormatDateAsNumber indicates that date values should be
|
|
|
|
// rendered as naked numbers (instead of as a string) if possible.
|
|
|
|
// See cli.OptDateFormatAsNumber.
|
|
|
|
FormatDateAsNumber bool
|
|
|
|
|
2023-08-04 08:41:33 +03:00
|
|
|
// ExcelDatetimeFormat is the format string for datetime values.
|
|
|
|
// See excelw.OptDatetimeFormat.
|
|
|
|
ExcelDatetimeFormat string
|
|
|
|
|
|
|
|
// ExcelDateFormat is the format string for date values.
|
|
|
|
// See excelw.OptDateFormat.
|
|
|
|
ExcelDateFormat string
|
|
|
|
|
|
|
|
// ExcelTimeFormat is the format string for time values.
|
|
|
|
// See excelw.OptTimeFormat.
|
|
|
|
ExcelTimeFormat string
|
|
|
|
|
2023-04-16 01:28:51 +03:00
|
|
|
// Active is the color for an active handle (or group, etc).
|
|
|
|
Active *color.Color
|
|
|
|
|
|
|
|
// Bold is the color for bold elements. Frequently Punc will just be color.Bold.
|
|
|
|
Bold *color.Color
|
|
|
|
|
2020-08-06 20:58:47 +03:00
|
|
|
// Bool is the color for boolean values.
|
|
|
|
Bool *color.Color
|
|
|
|
|
|
|
|
// Bytes is the color for byte / binary values.
|
|
|
|
Bytes *color.Color
|
|
|
|
|
|
|
|
// Datetime is the color for time-related values.
|
|
|
|
Datetime *color.Color
|
|
|
|
|
2023-05-19 17:24:18 +03:00
|
|
|
// DiffPlus is the color for diff plus "+" elements.
|
|
|
|
DiffPlus *color.Color
|
|
|
|
|
|
|
|
// DiffMinus is the color for diff minus "-" elements.
|
|
|
|
DiffMinus *color.Color
|
|
|
|
|
|
|
|
// DiffHeader is the color for diff header elements.
|
|
|
|
DiffHeader *color.Color
|
|
|
|
|
|
|
|
// DiffSection is the color for diff section elements.
|
|
|
|
DiffSection *color.Color
|
|
|
|
|
|
|
|
// DiffNormal is the color for regular diff text.
|
|
|
|
DiffNormal *color.Color
|
|
|
|
|
2024-01-15 04:45:34 +03:00
|
|
|
// Disabled is the color for disabled elements.
|
|
|
|
Disabled *color.Color
|
|
|
|
|
2023-04-26 18:16:42 +03:00
|
|
|
// Duration is the color for time duration values.
|
|
|
|
Duration *color.Color
|
|
|
|
|
2024-01-15 04:45:34 +03:00
|
|
|
// Enabled is the color for enabled elements.
|
|
|
|
Enabled *color.Color
|
|
|
|
|
2020-08-06 20:58:47 +03:00
|
|
|
// Error is the color for error elements such as an error message.
|
|
|
|
Error *color.Color
|
|
|
|
|
2023-04-16 01:28:51 +03:00
|
|
|
// Faint is the color for faint elements - the opposite of Hilite.
|
|
|
|
Faint *color.Color
|
|
|
|
|
|
|
|
// Handle is the color for source handles such as "@sakila"
|
2020-08-06 20:58:47 +03:00
|
|
|
Handle *color.Color
|
|
|
|
|
|
|
|
// Header is the color for header elements in a table.
|
|
|
|
Header *color.Color
|
|
|
|
|
|
|
|
// Hilite is the color for highlighted elements.
|
|
|
|
Hilite *color.Color
|
|
|
|
|
|
|
|
// Key is the color for keys such as a JSON field name.
|
|
|
|
Key *color.Color
|
|
|
|
|
2023-04-16 01:28:51 +03:00
|
|
|
// Location is the color for Source.Location values.
|
|
|
|
Location *color.Color
|
|
|
|
|
|
|
|
// Null is the color for null.
|
|
|
|
Null *color.Color
|
|
|
|
|
2023-05-01 06:59:34 +03:00
|
|
|
// Normal is the default color.
|
|
|
|
Normal *color.Color
|
|
|
|
|
2023-04-16 01:28:51 +03:00
|
|
|
// Number is the color for number values, including int, float, decimal etc.
|
|
|
|
Number *color.Color
|
|
|
|
|
2020-08-06 20:58:47 +03:00
|
|
|
// Punc is the color for punctuation such as colons, braces, etc.
|
|
|
|
Punc *color.Color
|
2023-04-16 01:28:51 +03:00
|
|
|
|
|
|
|
// String is the color for string values.
|
|
|
|
String *color.Color
|
|
|
|
|
|
|
|
// Success is the color for success elements.
|
|
|
|
Success *color.Color
|
2024-01-15 04:45:34 +03:00
|
|
|
|
|
|
|
// Stack is the color for stack traces.
|
|
|
|
Stack *color.Color
|
|
|
|
|
|
|
|
// StackError is the color for errors attached to a stack trace.
|
|
|
|
StackError *color.Color
|
|
|
|
|
|
|
|
// StackErrorType is the color for the error types attached to a stack trace.
|
|
|
|
StackErrorType *color.Color
|
|
|
|
|
|
|
|
// Warning is the color for warning elements.
|
|
|
|
Warning *color.Color
|
2020-08-06 20:58:47 +03:00
|
|
|
}
|
|
|
|
|
2023-04-22 06:36:32 +03:00
|
|
|
// NewPrinting returns a Printing instance. Color and pretty-print
|
2020-08-06 20:58:47 +03:00
|
|
|
// are enabled. The default indent is two spaces.
|
2023-04-22 06:36:32 +03:00
|
|
|
func NewPrinting() *Printing {
|
|
|
|
pr := &Printing{
|
2023-05-07 05:36:34 +03:00
|
|
|
ShowHeader: true,
|
|
|
|
Verbose: false,
|
|
|
|
Compact: false,
|
|
|
|
Redact: true,
|
|
|
|
FlushThreshold: 1000,
|
|
|
|
FormatDatetime: timez.FormatFunc(timez.DefaultDatetime),
|
|
|
|
FormatDatetimeAsNumber: false,
|
|
|
|
FormatTime: timez.FormatFunc(timez.DefaultTime),
|
|
|
|
FormatTimeAsNumber: false,
|
|
|
|
FormatDate: timez.FormatFunc(timez.DefaultDate),
|
|
|
|
FormatDateAsNumber: false,
|
|
|
|
monochrome: false,
|
|
|
|
Indent: " ",
|
|
|
|
Active: color.New(color.FgGreen, color.Bold),
|
|
|
|
Bold: color.New(color.Bold),
|
|
|
|
Bool: color.New(color.FgYellow),
|
|
|
|
Bytes: color.New(color.Faint),
|
|
|
|
Datetime: color.New(color.FgGreen, color.Faint),
|
2023-05-19 17:24:18 +03:00
|
|
|
DiffHeader: color.New(color.Bold),
|
|
|
|
DiffMinus: color.New(color.FgRed),
|
|
|
|
DiffNormal: color.New(color.Faint),
|
|
|
|
DiffPlus: color.New(color.FgGreen),
|
|
|
|
DiffSection: color.New(color.FgCyan),
|
2024-01-15 04:45:34 +03:00
|
|
|
Disabled: color.New(color.FgYellow, color.Faint),
|
2023-05-07 05:36:34 +03:00
|
|
|
Duration: color.New(color.FgGreen, color.Faint),
|
2024-01-15 04:45:34 +03:00
|
|
|
Enabled: color.New(color.FgGreen, color.Faint),
|
2023-05-07 05:36:34 +03:00
|
|
|
Error: color.New(color.FgRed, color.Bold),
|
|
|
|
Faint: color.New(color.Faint),
|
|
|
|
Handle: color.New(color.FgBlue),
|
|
|
|
Header: color.New(color.FgBlue),
|
|
|
|
Hilite: color.New(color.FgHiBlue),
|
|
|
|
Key: color.New(color.FgBlue, color.Bold),
|
|
|
|
Location: color.New(color.FgGreen),
|
|
|
|
Normal: color.New(),
|
|
|
|
Null: color.New(color.Faint),
|
|
|
|
Number: color.New(color.FgCyan),
|
|
|
|
Punc: color.New(color.Bold),
|
|
|
|
String: color.New(color.FgGreen),
|
2024-01-15 04:45:34 +03:00
|
|
|
Stack: color.New(color.Faint),
|
|
|
|
StackError: color.New(color.FgYellow, color.Faint),
|
|
|
|
StackErrorType: color.New(color.FgGreen, color.Faint),
|
2023-05-07 05:36:34 +03:00
|
|
|
Success: color.New(color.FgGreen, color.Bold),
|
2024-01-15 04:45:34 +03:00
|
|
|
Warning: color.New(color.FgYellow),
|
2020-08-06 20:58:47 +03:00
|
|
|
}
|
|
|
|
|
2023-04-22 06:36:32 +03:00
|
|
|
pr.EnableColor(true)
|
|
|
|
return pr
|
|
|
|
}
|
|
|
|
|
2023-05-22 18:08:14 +03:00
|
|
|
// Clone returns a clone of pr.
|
|
|
|
func (pr *Printing) Clone() *Printing {
|
|
|
|
pr2 := &Printing{
|
|
|
|
monochrome: pr.monochrome,
|
|
|
|
FlushThreshold: pr.FlushThreshold,
|
|
|
|
ShowHeader: pr.ShowHeader,
|
|
|
|
Verbose: pr.Verbose,
|
|
|
|
Compact: pr.Compact,
|
|
|
|
Indent: pr.Indent,
|
|
|
|
Redact: pr.Redact,
|
|
|
|
FormatDatetime: pr.FormatDatetime,
|
|
|
|
FormatDatetimeAsNumber: pr.FormatDatetimeAsNumber,
|
|
|
|
FormatTime: pr.FormatTime,
|
|
|
|
FormatTimeAsNumber: pr.FormatTimeAsNumber,
|
|
|
|
FormatDate: pr.FormatDate,
|
|
|
|
FormatDateAsNumber: pr.FormatDateAsNumber,
|
|
|
|
}
|
|
|
|
|
|
|
|
pr2.Active = lo.ToPtr(*pr.Active)
|
|
|
|
pr2.Bold = lo.ToPtr(*pr.Bold)
|
|
|
|
pr2.Bool = lo.ToPtr(*pr.Bool)
|
|
|
|
pr2.Bytes = lo.ToPtr(*pr.Bytes)
|
|
|
|
pr2.Datetime = lo.ToPtr(*pr.Datetime)
|
|
|
|
pr2.DiffPlus = lo.ToPtr(*pr.DiffPlus)
|
|
|
|
pr2.DiffMinus = lo.ToPtr(*pr.DiffMinus)
|
|
|
|
pr2.DiffHeader = lo.ToPtr(*pr.DiffHeader)
|
|
|
|
pr2.DiffSection = lo.ToPtr(*pr.DiffSection)
|
|
|
|
pr2.DiffNormal = lo.ToPtr(*pr.DiffNormal)
|
2024-01-15 04:45:34 +03:00
|
|
|
pr2.Disabled = lo.ToPtr(*pr.Disabled)
|
2023-05-22 18:08:14 +03:00
|
|
|
pr2.Duration = lo.ToPtr(*pr.Duration)
|
2024-01-15 04:45:34 +03:00
|
|
|
pr2.Enabled = lo.ToPtr(*pr.Enabled)
|
2023-05-22 18:08:14 +03:00
|
|
|
pr2.Error = lo.ToPtr(*pr.Error)
|
|
|
|
pr2.Faint = lo.ToPtr(*pr.Faint)
|
|
|
|
pr2.Handle = lo.ToPtr(*pr.Handle)
|
|
|
|
pr2.Header = lo.ToPtr(*pr.Header)
|
|
|
|
pr2.Hilite = lo.ToPtr(*pr.Hilite)
|
|
|
|
pr2.Key = lo.ToPtr(*pr.Key)
|
|
|
|
pr2.Location = lo.ToPtr(*pr.Location)
|
|
|
|
pr2.Null = lo.ToPtr(*pr.Null)
|
|
|
|
pr2.Normal = lo.ToPtr(*pr.Normal)
|
|
|
|
pr2.Number = lo.ToPtr(*pr.Number)
|
|
|
|
pr2.Punc = lo.ToPtr(*pr.Punc)
|
|
|
|
pr2.String = lo.ToPtr(*pr.String)
|
|
|
|
pr2.Success = lo.ToPtr(*pr.Success)
|
2024-01-15 04:45:34 +03:00
|
|
|
pr2.Stack = lo.ToPtr(*pr.Stack)
|
|
|
|
pr2.StackError = lo.ToPtr(*pr.StackError)
|
|
|
|
pr2.StackErrorType = lo.ToPtr(*pr.StackErrorType)
|
|
|
|
pr2.Warning = lo.ToPtr(*pr.Warning)
|
2023-05-22 18:08:14 +03:00
|
|
|
|
|
|
|
return pr2
|
|
|
|
}
|
|
|
|
|
2023-05-03 15:36:10 +03:00
|
|
|
// LogValue implements slog.LogValuer.
|
|
|
|
func (pr *Printing) LogValue() slog.Value {
|
|
|
|
if pr == nil {
|
|
|
|
return slog.Value{}
|
|
|
|
}
|
|
|
|
|
2023-08-04 08:41:33 +03:00
|
|
|
// REVISIT: Should we output all Printing values here?
|
2023-05-03 15:36:10 +03:00
|
|
|
return slog.GroupValue(
|
|
|
|
slog.Bool("verbose", pr.Verbose),
|
|
|
|
slog.Bool("header", pr.ShowHeader),
|
|
|
|
slog.Bool("monochrome", pr.monochrome),
|
2023-05-05 20:41:22 +03:00
|
|
|
slog.Bool("compact", pr.Compact),
|
2023-05-03 15:36:10 +03:00
|
|
|
slog.Bool("redact", pr.Redact),
|
|
|
|
slog.Int("flush-threshold", pr.FlushThreshold),
|
|
|
|
slog.String("indent", pr.Indent),
|
2023-05-07 05:36:34 +03:00
|
|
|
slog.Bool("format.datetime.number", pr.FormatDatetimeAsNumber),
|
|
|
|
slog.Bool("format.date.number", pr.FormatDateAsNumber),
|
|
|
|
slog.Bool("format.time.number", pr.FormatTimeAsNumber),
|
2023-05-03 15:36:10 +03:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-04-22 06:36:32 +03:00
|
|
|
func (pr *Printing) colors() []*color.Color {
|
|
|
|
return []*color.Color{
|
2023-04-26 18:16:42 +03:00
|
|
|
pr.Active, pr.Bold, pr.Bold, pr.Bytes, pr.Datetime, pr.Duration,
|
2023-05-19 17:24:18 +03:00
|
|
|
pr.DiffHeader, pr.DiffMinus, pr.DiffPlus, pr.DiffNormal, pr.DiffSection,
|
2024-01-15 04:45:34 +03:00
|
|
|
pr.Disabled, pr.Enabled,
|
2023-04-22 06:36:32 +03:00
|
|
|
pr.Error, pr.Faint, pr.Handle, pr.Header, pr.Hilite,
|
2024-01-15 04:45:34 +03:00
|
|
|
pr.Key, pr.Location, pr.Normal, pr.Null, pr.Number, pr.Punc,
|
|
|
|
pr.Stack, pr.StackError, pr.StackErrorType,
|
|
|
|
pr.String, pr.Success, pr.Warning,
|
2023-04-22 06:36:32 +03:00
|
|
|
}
|
2020-08-06 20:58:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// IsMonochrome returns true if in monochrome (no color) mode.
|
|
|
|
// Default is false (color enabled) for a new instance.
|
2023-04-22 06:36:32 +03:00
|
|
|
func (pr *Printing) IsMonochrome() bool {
|
|
|
|
return pr.monochrome
|
2020-08-06 20:58:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// EnableColor enables or disables all colors.
|
2023-04-22 06:36:32 +03:00
|
|
|
func (pr *Printing) EnableColor(enable bool) {
|
2020-08-06 20:58:47 +03:00
|
|
|
if enable {
|
2023-04-22 06:36:32 +03:00
|
|
|
pr.monochrome = false
|
|
|
|
|
|
|
|
for _, clr := range pr.colors() {
|
|
|
|
clr.EnableColor()
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
pr.monochrome = true
|
|
|
|
for _, clr := range pr.colors() {
|
|
|
|
clr.DisableColor()
|
2020-08-06 20:58:47 +03:00
|
|
|
}
|
|
|
|
}
|