Documentation
¶
Overview ¶
Package result owns the primary output channel — the structured-data stream the framework emits to the user (or to downstream tooling, when piped). Distinct from the side channel (pkg/status) that carries categorized narration.
The pipeline shape is `value → result.Filter → result.Formatter → sink.Sink`. NewPipeline composes the three stages; callers Emit values through it. Three formatters ship today (JSONFormatter, YAMLFormatter, CSVFormatter) plus TemplateFormatter for caller-supplied Go templates. Three filters ship: NoOpFilter pass-through, FieldFilter selection, JQFilter for jq expressions.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type CSVFormatter ¶
type CSVFormatter struct{}
CSVFormatter renders a slice of rows as RFC 4180 comma-separated values.
The shape of value drives header inference:
- If value implements HasHeaders, its Headers() result is the column order.
- Otherwise, if value is a slice/array of structs, the struct fields (in declaration order, with `csv:"name"` overrides and `csv:"-"` skips) are the columns.
- Otherwise, if value is a slice/array of maps, the union of map keys (sorted alphabetically) is the columns.
- Otherwise, [Format] returns an error.
Cell values are rendered via fmt.Sprint, which honors fmt.Stringer for custom types and produces reasonable defaults for numbers, bools, time.Time, and nil. Quoting is handled by encoding/csv per RFC 4180. Empty input renders no bytes (no header row).
func (CSVFormatter) Format ¶
func (CSVFormatter) Format(value any, w io.Writer) error
Format renders value as RFC 4180 CSV to w.
Returns an error if value is not a slice/array of structs or maps and does not implement HasHeaders.
type FieldFilter ¶
type FieldFilter struct {
// contains filtered or unexported fields
}
FieldFilter narrows a slice of structs/maps to those whose named field matches a target value. The expression form is `field=value`, mirroring `gh`'s `--filter` flag and `kubectl`'s field selectors. Multiple expressions AND together — every expression must match for a row to pass.
Comparison is done after rendering both sides through fmt.Sprint, so primitives, fmt.Stringer implementations, and time.Time values all compare correctly. Numeric strings ("42") and numeric values (42) compare equal — the expression dialect is string-typed.
Construct via NewFieldFilter; surface parse errors directly to the user.
func NewFieldFilter ¶
func NewFieldFilter(exprs ...string) (*FieldFilter, error)
NewFieldFilter parses zero or more `field=value` expressions into a FieldFilter. Empty expressions are skipped silently, supporting the common pattern where the caller does `strings.Split(flag, ",")` against an empty flag value. An expression missing `=` is a parse error.
Parameters:
- exprs: zero or more `field=value` expressions.
Returns:
- *FieldFilter: the constructed filter. With zero predicates, Filter.Apply is a pass-through.
- error: when any expression fails to parse.
type Filter ¶
type Filter interface {
// Apply returns a (possibly modified) value, or an error if the filter expression is invalid
// for the input shape.
Apply(value any) (any, error)
}
Filter is the selection stage of the result pipeline. Implementations narrow, transform, or pass-through the value before it reaches the formatter.
func FilterByExprs ¶
FilterByExprs returns a composed Filter from optional `field=value` expressions and an optional jq expression. When both are present, the field filter runs first (cheap predicate elimination), then the jq filter (full transform). With both empty, returns a NoOpFilter.
Parameters:
- fieldExprs: zero or more `field=value` expressions for FieldFilter.
- jqExpr: an optional jq expression for JQFilter; empty disables the jq stage.
Returns:
- Filter: the composed filter.
- error: when any expression fails to parse.
type Formatter ¶
type Formatter interface {
// Format renders value to w in the implementation's format (JSON, YAML, CSV, template, etc.).
Format(value any, w io.Writer) error
}
Formatter is the rendering stage of the result pipeline. Implementations encode the (filtered) value as bytes on the writer.
The writer parameter is typed as io.Writer rather than sink.Sink because formatters operate on a byte stream — they don't need TTY-awareness, Close lifecycle, or any other Sink-specific concerns. Pipeline.Emit passes its sink.Sink (which is an io.Writer) to Format directly; stdlib encoders (encoding/json, gopkg.in/yaml.v3, etc.) plug in without adapter shims.
func FormatterByName ¶
FormatterByName returns the Formatter registered under name. The known names are "json", "yaml", "csv", and "template". The "template" form requires a non-empty templateText; the others ignore it.
Parameters:
- name: the formatter name; case-insensitive.
- templateText: the body for the "template" formatter; ignored otherwise.
Returns:
- Formatter: the constructed formatter.
- error: when name is unknown or when the template fails to parse.
type HasHeaders ¶
type HasHeaders interface {
// Headers returns the column order to use when rendering the receiver as CSV.
Headers() []string
}
HasHeaders is the opt-in interface that overrides automatic header inference. Implementations are typically named-slice types whose row shape doesn't lend itself to reflection (e.g., heterogeneous row content keyed by symbolic names).
type JQFilter ¶
type JQFilter struct {
// contains filtered or unexported fields
}
JQFilter applies a jq expression to the value via github.com/itchyny/gojq.
The input is normalized to gojq's native shape (map[string]any / []any / primitives) by round-tripping through encoding/json. This costs a serialization pass per Apply but lets callers hand any JSON-serializable Go value to the filter without first converting it.
The expression is parsed and compiled at construction; parse errors surface there. Execution errors from gojq propagate through Apply. Single-result expressions return the unwrapped value; multi-result expressions (jq's `,` operator, `..` recursive descent, etc.) collect into a slice.
func NewJQFilter ¶
NewJQFilter parses and compiles a jq expression into a JQFilter.
Parameters:
- expression: the jq query, e.g. ".[] | select(.kind == \"file\")".
Returns:
- *JQFilter: the constructed filter.
- error: when expression fails to parse or compile.
func (*JQFilter) Apply ¶
Apply normalizes value to gojq's native shape, runs the compiled query, and returns the result.
A single-result expression returns the unwrapped value. A multi-result expression collects every emitted value into an []any. An expression that emits no values returns nil. Execution errors are returned as-is.
type JSONFormatter ¶
type JSONFormatter struct{}
JSONFormatter renders the value as indented JSON. Two-space indentation; no HTML escaping concerns (the stream is for tooling consumption, not browser embedding).
type NoOpFilter ¶
type NoOpFilter struct{}
NoOpFilter is the pass-through Filter; Apply returns its argument unchanged.
type Pipeline ¶
type Pipeline struct {
// contains filtered or unexported fields
}
Pipeline composes a Filter and a Formatter against a sink.Sink. Callers Emit values; the Pipeline applies the filter, hands the result to the formatter, and the formatter writes through the sink.
All fields are unexported and set at construction by NewPipeline; the value is immutable from the caller's perspective. To suppress all output, construct with sink.Discard as the sink.
func NewPipeline ¶
NewPipeline constructs an immutable Pipeline writing through the supplied sink.
Parameters:
- filter: the selection stage. Pass nil to use NoOpFilter pass-through.
- formatter: the rendering stage. Must not be nil.
- s: the sink.Sink to write through. Must not be nil. Pass sink.Discard to suppress all emissions; pass sink.Stdout for the standard cli case.
Returns:
- *Pipeline: the constructed pipeline.
type TemplateFormatter ¶
type TemplateFormatter struct {
// contains filtered or unexported fields
}
TemplateFormatter renders the value through a text/template.Template. The template text is supplied at construction; the value passed to [Format] is the template's `.` (dot) data binding.
Construct via NewTemplateFormatter. The template is parsed once at construction; parse errors surface there rather than per-Emit. Callers passing user-supplied template text should surface parse errors directly to the user (no silent fallback).
func NewTemplateFormatter ¶
func NewTemplateFormatter(text string) (*TemplateFormatter, error)
NewTemplateFormatter parses text into a text/template.Template and returns a Formatter that applies it to each emitted value. The template's name is "result" — referenced by other templates via {{template "result" .}}. Functions registered via the standard text/template helpers are not pre-installed; callers needing helpers can construct a template separately and pass it via NewTemplateFormatterFromTemplate.
Parameters:
- text: the template body.
Returns:
- *TemplateFormatter: the formatter.
- error: when text fails to parse.
func NewTemplateFormatterFromTemplate ¶
func NewTemplateFormatterFromTemplate(tmpl *template.Template) *TemplateFormatter
NewTemplateFormatterFromTemplate wraps an already-constructed text/template.Template. Use this when the caller needs to register custom Funcs, parse multiple files, or otherwise configure the template beyond what NewTemplateFormatter supports.
Parameters:
- tmpl: the pre-constructed template; must not be nil.
Returns:
- *TemplateFormatter: the formatter.
type YAMLFormatter ¶
type YAMLFormatter struct{}
YAMLFormatter renders the value as indented YAML. Two-space indentation; latest stable yaml.v3 from go-yaml.