byob-go-cli

TTY-adaptive table printer: one API, two render paths

byob-output.1 output

Problem: a list command that prints a pretty ANSI table breaks every pipeline. A list command that prints tab-separated values is unreadable in a terminal. Users want both, and --output=plain is a clumsy workaround.

Idea: one table printer, two render paths. On Render(), branch on IsStdoutTTY(). TTY path: colored headers, auto-width columns, fuzzy timestamps ("2h ago"). Non-TTY path: tab-separated columns, RFC3339 timestamps, no color. Users never pass a flag; the tool does the right thing based on where stdout points.

Tradeoffs: authors must remember timestamps, widths, and colors diverge between paths. Push the divergence into the printer; commands stay simple.

Design

type Table struct {
    io      *iostreams.IOStreams
    headers []string
    rows    [][]string
}

func (t *Table) AddRow(cells ...string) { t.rows = append(t.rows, cells) }

func (t *Table) Render() error {
    if t.io.IsStdoutTTY() {
        return t.renderTTY()   // padded, colored, fuzzy time
    }
    return t.renderTSV()       // tab-separated, RFC3339
}

func (t *Table) renderTSV() error {
    for _, r := range t.rows {
        fmt.Fprintln(t.io.Out, strings.Join(r, "\t"))
    }
    return nil
}