fj/internal/iostreams/table.go

65 lines
1.6 KiB
Go
Raw Permalink Normal View History

package iostreams
import (
"fmt"
"strings"
"text/tabwriter"
)
// TablePrinter prints TTY-aware tables. In TTY mode it uses aligned columns with
// bold headers. In pipe mode it emits tab-separated values without headers.
type TablePrinter struct {
ios *IOStreams
headers []string
rows [][]string
}
// NewTablePrinter creates a TablePrinter that writes to ios.Out.
func NewTablePrinter(ios *IOStreams) *TablePrinter {
return &TablePrinter{
ios: ios,
}
}
// AddHeader sets the column headers. Headers are only displayed in TTY mode.
func (t *TablePrinter) AddHeader(headers ...string) {
t.headers = headers
}
// AddRow appends a row of fields to the table.
func (t *TablePrinter) AddRow(fields ...string) {
t.rows = append(t.rows, fields)
}
// Render writes the table to the IOStreams output. In TTY mode it uses tabwriter
// with bold headers. In pipe mode it emits tab-separated values without headers.
func (t *TablePrinter) Render() error {
if !t.ios.IsStdoutTTY() {
// Pipe mode: tab-separated, no headers
for _, row := range t.rows {
if _, err := fmt.Fprintln(t.ios.Out, strings.Join(row, "\t")); err != nil {
return err
}
}
return nil
}
// TTY mode: use tabwriter with aligned columns
w := tabwriter.NewWriter(t.ios.Out, 0, 0, 2, ' ', 0)
if len(t.headers) > 0 {
cs := t.ios.ColorScheme()
boldHeaders := make([]string, len(t.headers))
for i, h := range t.headers {
boldHeaders[i] = cs.Bold(h)
}
fmt.Fprintln(w, strings.Join(boldHeaders, "\t"))
}
for _, row := range t.rows {
fmt.Fprintln(w, strings.Join(row, "\t"))
}
return w.Flush()
}