Documentation
¶
Overview ¶
Package output provides structured, themeable output formatting for CLI tools.
A single Renderer façade is the entry point. It is configured once — with a destination writer, an output Format, a Theme, and whether output is interactive — and every method reads that configuration:
- Renderer.Write emits arbitrary data in the configured format (text via a caller-supplied function; JSON/YAML by marshalling; CSV/TSV/Markdown as a table).
- Renderer.Table renders a slice of structs or maps as an aligned table or a machine-readable format.
- Renderer.Render renders Markdown to styled ANSI via glamour.
- Renderer.Emit / Renderer.EmitError write the standard JSON Response envelope for machine consumption.
- Renderer.Status, Renderer.Progress and Renderer.Spin provide live progress feedback that degrades to plain lines in non-interactive environments.
The package is framework-agnostic: it takes an io.Writer and a Format, not a CLI command. Integration with spf13/cobra — reading the --output flag and the command's writer — lives in the opt-in subpackage gitlab.com/phpboyscout/go/output/cobra so that importing the core never pulls cobra (or pflag) into the dependency graph.
Index ¶
- Constants
- func IsInteractive() bool
- func RenderMarkdown(content string) string
- func SpinWithResult[T any](r *Renderer, ctx context.Context, msg string, ...) (T, error)
- type Column
- type Format
- type Option
- type Progress
- type Renderer
- func (r *Renderer) Emit(resp Response) error
- func (r *Renderer) EmitError(command string, err error) error
- func (r *Renderer) Format() Format
- func (r *Renderer) IsJSON() bool
- func (r *Renderer) Progress(total int, description string) *Progress
- func (r *Renderer) Render(markdown string) error
- func (r *Renderer) Spin(ctx context.Context, msg string, fn func(ctx context.Context) error) error
- func (r *Renderer) Status() *Status
- func (r *Renderer) Table(rows any, opts ...TableOption) error
- func (r *Renderer) Write(data any, textFunc func(io.Writer)) error
- type Response
- type Status
- type TableOption
- type Theme
Examples ¶
Constants ¶
const ( StatusSuccess = "success" StatusError = "error" StatusWarning = "warning" )
Status constants for the Response envelope.
Variables ¶
This section is empty.
Functions ¶
func IsInteractive ¶
func IsInteractive() bool
IsInteractive reports whether stdout is a TTY and CI mode is not active. It is the default interactivity detector a Renderer uses when WithInteractive is not supplied, and is exported so callers can reuse the same heuristic.
func RenderMarkdown ¶
RenderMarkdown renders Markdown content to styled ANSI terminal output via glamour. It detects the terminal width automatically, falling back to 80 columns, and returns the original content unchanged if glamour fails.
func SpinWithResult ¶
func SpinWithResult[T any](r *Renderer, ctx context.Context, msg string, fn func(ctx context.Context) (T, error)) (T, error)
SpinWithResult is like Renderer.Spin but returns a value alongside the error. It is a free function because Go methods cannot introduce a type parameter.
As with Spin, a Ctrl-C interrupt while the spinner is active cancels fn's context and returns (zero, context.Canceled).
Example ¶
package main
import (
"context"
"gitlab.com/phpboyscout/go/output"
)
func main() {
r := output.New()
result, err := output.SpinWithResult(r, context.Background(), "Fetching version",
func(_ context.Context) (string, error) {
return "v1.2.3", nil
})
if err == nil {
_ = result
}
}
Output:
Types ¶
type Column ¶
type Column struct {
// Header is the display name shown in the table header row.
Header string
// Field is the struct field name or map key to extract the value from.
Field string
// Width is the fixed column width. Zero means auto-sized to content.
Width int
// Sortable indicates this column can be used as a sort key.
Sortable bool
// Formatter is an optional function to format the cell value.
Formatter func(any) string
}
Column defines a single table column for Renderer.Table.
type Format ¶
type Format string
Format represents the output format for a Renderer.
const ( // FormatText is the default human-readable output format. FormatText Format = "text" // FormatJSON produces machine-readable JSON output. FormatJSON Format = "json" // FormatYAML produces machine-readable YAML output. FormatYAML Format = "yaml" // FormatCSV produces comma-separated values output. FormatCSV Format = "csv" // FormatMarkdown produces a pipe-delimited Markdown table with header separators. FormatMarkdown Format = "markdown" // FormatTSV produces tab-separated values output for shell pipelines. FormatTSV Format = "tsv" )
func Formats ¶
func Formats() []Format
Formats returns every supported Format in a stable order. It is the canonical value set for a CLI's --output flag.
func ParseFormat ¶
ParseFormat maps a case-insensitive string to a Format, falling back to FormatText for the empty string or any unrecognised value. The second return value reports whether the input named a known format.
type Option ¶
type Option func(*config)
Option configures a Renderer.
func WithFormat ¶
WithFormat sets the output Format. The default is FormatText.
func WithInteractive ¶
WithInteractive forces interactive (v true) or plain (v false) rendering, overriding the automatic IsInteractive detection.
func WithWriter ¶
WithWriter sets the destination writer. The default is os.Stderr for the progress-family renderers; structured output (Renderer.Write, Renderer.Table, Renderer.Emit) also writes here, so a caller emitting machine-readable output should point this at stdout.
type Progress ¶
type Progress struct {
// contains filtered or unexported fields
}
Progress tracks progress of a known-total operation. In interactive terminals it draws an animated bar; in non-interactive environments it logs periodic status lines. Obtain one from Renderer.Progress. Safe for concurrent use from multiple goroutines.
func (*Progress) Done ¶
func (p *Progress) Done()
Done marks the progress as complete and cleans up the display.
func (*Progress) Increment ¶
func (p *Progress) Increment()
Increment advances the progress by one unit.
func (*Progress) IncrementBy ¶
IncrementBy advances the progress by n units.
type Renderer ¶
type Renderer struct {
// contains filtered or unexported fields
}
Renderer is the configured façade for all output. Construct it with New and reuse it; every method reads the writer, format, theme and interactivity fixed at construction. A Renderer is safe for concurrent use only insofar as its underlying writer is; the progress-family helpers it returns carry their own locking.
func New ¶
New builds a Renderer from the default configuration and the given options. Defaults: writer os.Stderr, format FormatText, DefaultTheme, and interactivity auto-detected via IsInteractive.
func (*Renderer) Emit ¶
Emit writes resp as an indented JSON envelope to the configured writer when the format is FormatJSON; otherwise it is a no-op. It is the machine-readable counterpart to a command's human-readable text output.
Example ¶
package main
import (
"bytes"
"fmt"
"gitlab.com/phpboyscout/go/output"
)
func main() {
var buf bytes.Buffer
r := output.New(output.WithWriter(&buf), output.WithFormat(output.FormatJSON))
_ = r.Emit(output.Response{
Status: output.StatusSuccess,
Command: "version",
Data: map[string]string{"version": "v1.2.3"},
})
fmt.Print(buf.String())
}
Output: { "status": "success", "command": "version", "data": { "version": "v1.2.3" } }
func (*Renderer) EmitError ¶
EmitError builds an error Response envelope for command and emits it via Renderer.Emit.
func (*Renderer) IsJSON ¶
IsJSON reports whether the configured format is FormatJSON.
func (*Renderer) Progress ¶
Progress returns a progress indicator bound to the renderer's writer, theme and interactivity, with the given total and description.
func (*Renderer) Render ¶
Render writes Markdown to the configured writer using glamour styling. In JSON mode it is a no-op — callers should use Renderer.Write or Renderer.Emit for machine-readable output.
func (*Renderer) Spin ¶
Spin shows a spinner with a message while fn executes, using the renderer's writer, interactivity and theme. In interactive terminals it animates; in non-interactive environments (CI, piped output) it prints plain status lines. The context is passed to fn and used for cancellation.
If the user interrupts an active spinner with Ctrl-C, fn's context is cancelled and Spin returns context.Canceled rather than reporting a false success.
Example ¶
package main
import (
"context"
"fmt"
"time"
"gitlab.com/phpboyscout/go/output"
)
func main() {
// Spin shows a spinner while the function executes. In non-interactive
// environments (CI, piped output) it prints plain status lines.
r := output.New()
err := r.Spin(context.Background(), "Loading data", func(_ context.Context) error {
time.Sleep(time.Millisecond)
return nil
})
if err != nil {
fmt.Println("Error:", err)
}
}
Output:
func (*Renderer) Status ¶
Status returns a live status display bound to the renderer's writer, theme and interactivity.
func (*Renderer) Table ¶
func (r *Renderer) Table(rows any, opts ...TableOption) error
Table renders rows as an aligned table (text) or a machine-readable format, honouring the renderer's configured Format. rows must be a slice of structs (columns derived from `table:` tags) or a slice of maps with explicit WithColumns.
Example ¶
package main
import (
"bytes"
"fmt"
"gitlab.com/phpboyscout/go/output"
)
func main() {
type release struct {
Name string `table:"NAME,sortable"`
Version string `table:"VERSION"`
Status string `table:"STATUS"`
}
rows := []release{
{Name: "my-tool", Version: "v1.2.3", Status: "up to date"},
{Name: "other", Version: "v0.9.1", Status: "update available"},
}
var buf bytes.Buffer
r := output.New(output.WithWriter(&buf), output.WithFormat(output.FormatText))
if err := r.Table(rows, output.WithNoTruncation()); err != nil {
fmt.Println("Error:", err)
return
}
fmt.Print(buf.String())
}
Output: NAME VERSION STATUS my-tool v1.2.3 up to date other v0.9.1 update available
func (*Renderer) Write ¶
Write emits data in the configured format:
- FormatJSON / FormatYAML: data is marshalled directly.
- FormatText: textFunc is called to produce human-readable output.
- FormatCSV / FormatTSV / FormatMarkdown: data is rendered as a table (it must be a slice of structs or maps, as for Renderer.Table).
textFunc may be nil when the configured format is not text.
type Response ¶
type Response struct {
Status string `json:"status"`
Command string `json:"command"`
Data any `json:"data,omitempty"`
Error string `json:"error,omitempty"`
}
Response is the standard JSON envelope for machine-readable command output.
type Status ¶
type Status struct {
// contains filtered or unexported fields
}
Status displays a live-updating status message for multi-step operations. In interactive terminals it updates the current line in place; in non-interactive environments it prints sequential lines. Obtain one from Renderer.Status. Safe for concurrent use from multiple goroutines.
type TableOption ¶
type TableOption func(*tableConfig)
TableOption configures a Renderer.Table call.
func WithColumns ¶
func WithColumns(cols ...Column) TableOption
WithColumns explicitly defines the table columns. When not provided, columns are derived from `table:` struct tags on the row data type.
func WithMaxWidth ¶
func WithMaxWidth(width int) TableOption
WithMaxWidth overrides automatic terminal width detection.
func WithNoHeader ¶
func WithNoHeader() TableOption
WithNoHeader suppresses the header row in text table output.
func WithNoTruncation ¶
func WithNoTruncation() TableOption
WithNoTruncation disables terminal-width truncation. Useful when output is piped to a file or another process.
func WithSortBy ¶
func WithSortBy(field string) TableOption
WithSortBy sets the column to sort rows by. The column must be marked Sortable.
func WithSortDescending ¶
func WithSortDescending() TableOption
WithSortDescending reverses the sort order.
type Theme ¶
type Theme struct {
// SuccessIcon, WarnIcon and FailIcon mark a completed step's outcome.
SuccessIcon string
WarnIcon string
FailIcon string
// SuccessStyle, WarnStyle and FailStyle style the icon + message of a
// completed step.
SuccessStyle lipgloss.Style
WarnStyle lipgloss.Style
FailStyle lipgloss.Style
// SpinnerStyle styles the animated spinner frames.
SpinnerStyle lipgloss.Style
// SpinnerFrames is the animation frame set shared by [Renderer.Spin] and the
// working state of [Renderer.Status]. It must contain at least one frame.
SpinnerFrames []string
// ProgressFilled and ProgressEmpty are the runes used to draw a progress bar.
ProgressFilled rune
ProgressEmpty rune
// ProgressHead is the rune drawn at the leading edge of a partially-filled
// bar. A zero value falls back to ProgressFilled.
ProgressHead rune
}
Theme controls the icons and styles the progress-family renderers (Renderer.Status, Renderer.Progress, Renderer.Spin) use. Downstream tools override it via WithTheme to match their own palette.
func DefaultTheme ¶
func DefaultTheme() Theme
DefaultTheme returns the built-in theme: check/warning/cross icons, a magenta spinner, and an ASCII progress bar. It is used when WithTheme is not given.