Documentation
¶
Overview ¶
Package cliutil provides output rendering and cobra flag bundles for building ConfigHub CLIs and `cub` plugins with a consistent UX. It depends only on cobra, gojq, and yqkit — not on the ConfigHub API client — so API-only tools don't pay for it and UX-only code doesn't drag in the client. Combine it with github.com/confighub/sdk/core/cubapi to build a full tool.
It has no global state: every function takes its inputs (an io.Writer, an OutputSpec, a *cobra.Command) explicitly.
Index ¶
- func ColumnHeader(col string) string
- func Dash(s string) string
- func Fprintln(w io.Writer, a ...any)
- func OutputFlag(cmd *cobra.Command, dest *string, defaultValue string)
- func PrintJSON(w io.Writer, v any) error
- func PrintYAML(w io.Writer, v any) error
- func ProfilesSpaceFlag(cmd *cobra.Command, dest *string, defaultSpace string)
- func RenderJQ(w io.Writer, v any, expr string) error
- func RenderTable[T any](w io.Writer, entities []*T, columns, defaultColumns []string, ...) error
- func RenderYQ(w io.Writer, v any, expr string) error
- func YesNo(b bool) string
- type ColumnProvider
- type CommitFlags
- type OutputKind
- type OutputSpec
- type QueryFlags
- func (q *QueryFlags) Bind(cmd *cobra.Command)
- func (q *QueryFlags) BindSelect(cmd *cobra.Command)
- func (q *QueryFlags) BindSpaceLabels(cmd *cobra.Command)
- func (q *QueryFlags) BindStored(cmd *cobra.Command)
- func (q *QueryFlags) BindWhere(cmd *cobra.Command)
- func (q *QueryFlags) Label(flag, spaceLabel, usage string)
- func (q QueryFlags) Predicate() (string, error)
- type TableOptions
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ColumnHeader ¶
ColumnHeader converts a column path to a display header: CamelCase becomes HYPHEN-CASE and Labels.X / Annotations.X become LABEL:X / ANNOTATION:X.
func Dash ¶ added in v0.4.5
Dash renders an empty value as "-", so a table column reads as absent rather than blank -- a cluster-scoped resource's namespace, an unset owner.
func Fprintln ¶ added in v0.4.5
Fprintln writes a line to w, discarding the error the way a CLI's final output can: there is nowhere left to report a failed write to, and a command that returned one would report a failure for output it had already produced.
func OutputFlag ¶
OutputFlag registers the standard -o/--output flag, binding it to dest. If defaultValue is empty, "json" is used (the agent-friendly default).
func PrintYAML ¶
PrintYAML writes v as YAML.
It marshals through JSON, so the YAML is the JSON projection of v in YAML syntax: the same field names, the same omissions, the same rendering of a type with a custom JSON marshaler. Marshaling to YAML directly would read `yaml:` struct tags instead, and a model tagged for an API that speaks JSON carries none -- so its fields would come out lowercased with every empty one present, describing the same value differently depending on which flag was passed.
func ProfilesSpaceFlag ¶ added in v0.4.5
ProfilesSpaceFlag registers --profiles-space, the Space holding a tool's stored profile Invocations. defaultSpace is what the flag defaults to; tools that share a profile library pass the same one, so that an operator has one place to look rather than one per tool they happen to have installed.
func RenderJQ ¶
RenderJQ evaluates a jq expression against v (marshaled to JSON) and writes each result to w — scalars on their own line, structured values as JSON.
func RenderTable ¶
func RenderTable[T any](w io.Writer, entities []*T, columns, defaultColumns []string, opts TableOptions) error
RenderTable writes entities as a column table to w. columns is the list of dotted field paths; if empty, defaultColumns is used. Headers derive from ColumnHeader unless opts.NoHeader is set.
Types ¶
type ColumnProvider ¶
type ColumnProvider struct {
// contains filtered or unexported fields
}
ColumnProvider renders columns from arbitrary structs by dotted field path, using reflection. It understands the ConfigHub "Extended<Entity>" envelope convention (e.g. ExtendedUnit{Unit: *Unit}): a bare field name is looked up on the envelope and, failing that, on the embedded entity. It also resolves Labels.<key> / Annotations.<key> against map fields. The zero value is unusable; construct one with NewColumnProvider.
func NewColumnProvider ¶
func NewColumnProvider(entity any) *ColumnProvider
NewColumnProvider creates a provider for the type of entity (typically a pointer to a struct), with common aliases (Name -> Slug) and formatters for time.Time and uuid.UUID.
func (*ColumnProvider) GetValue ¶
func (p *ColumnProvider) GetValue(obj any, fieldPath string) string
GetValue returns the string value of fieldPath on obj.
func (*ColumnProvider) WithAliases ¶
func (p *ColumnProvider) WithAliases(aliases map[string]string) *ColumnProvider
WithAliases adds column-name aliases (alias -> real field path).
func (*ColumnProvider) WithCustomColumns ¶
func (p *ColumnProvider) WithCustomColumns(custom map[string]func(any) string) *ColumnProvider
WithCustomColumns adds named columns computed by a function over the entity.
type CommitFlags ¶
CommitFlags carry the dry-run/commit controls shared by mutating commands. Mutations are dry-run by default; --commit performs the write and requires --change-desc for provenance.
func (*CommitFlags) Bind ¶
func (c *CommitFlags) Bind(cmd *cobra.Command)
Bind registers --commit and --change-desc on cmd.
func (CommitFlags) Validate ¶
func (c CommitFlags) Validate(summary string) (changeDesc string, dryRun bool, err error)
Validate enforces the commit policy and returns the change description to record (empty on dry-run) plus whether this is a dry-run. summary is suggested in the error when --change-desc is missing on commit.
type OutputKind ¶
type OutputKind int
OutputKind enumerates the supported -o/--output formats.
const ( OutputDefault OutputKind = iota OutputJSON OutputYAML OutputName OutputJQ OutputYQ OutputWide OutputCustomColumns OutputTable )
type OutputSpec ¶
type OutputSpec struct {
Kind OutputKind
Arg string
}
OutputSpec is the parsed form of an --output value. Arg is populated only for formats that take an expression or spec (jq, yq, custom-columns).
func ParseOutput ¶
func ParseOutput(s string) (OutputSpec, error)
ParseOutput parses an --output/-o value into an OutputSpec. Supported values: "" (default), json, yaml, name, wide, table, jq=<expr>, yq=<expr>, custom-columns=<spec>.
type QueryFlags ¶
type QueryFlags struct {
Where string
Filter string
Contains string
Select string
// Space scopes, registered by [QueryFlags.BindSpaceLabels]. Component names
// the Component the Space is a Variant of, by slug or ID; the rest are the
// standard Space labels, the ones the `cub variant` commands use.
Component string
Environment string
Region string
Owner string
Layer string
Variant string
// ResolveComponent returns the ComponentID of the Component with the given
// slug. This package does not depend on the API client, so a tool that binds
// --component sets it from its own; without it, --component accepts only an ID.
ResolveComponent func(slug string) (string, error)
// contains filtered or unexported fields
}
QueryFlags are the standard read/list scoping flags, plus the Space-label shorthands that spell a common scope without writing a predicate.
Bind them all with QueryFlags.Bind and QueryFlags.BindSpaceLabels, or bind the groups a command actually supports: a command whose query pins its own --select, for instance, wants QueryFlags.BindWhere rather than the field registered for a caller to override.
QueryFlags.Predicate compiles --where and the label shorthands into one expression. Filter, Contains and Select are not part of it: they are separate query parameters, not clauses.
func (*QueryFlags) Bind ¶
func (q *QueryFlags) Bind(cmd *cobra.Command)
Bind registers --where, --filter, --contains, and --select on cmd. The Space-label shorthands are registered separately, by QueryFlags.BindSpaceLabels.
func (*QueryFlags) BindSelect ¶ added in v0.4.5
func (q *QueryFlags) BindSelect(cmd *cobra.Command)
BindSelect registers --select. A command whose query needs particular fields to build its own model should not register it: a caller who narrowed the selection would get those fields back zeroed, with nothing to say they were never fetched.
func (*QueryFlags) BindSpaceLabels ¶ added in v0.4.5
func (q *QueryFlags) BindSpaceLabels(cmd *cobra.Command)
BindSpaceLabels registers the Space-label shorthands: --component, --environment, --region, --owner, --layer, --variant, and any label added with QueryFlags.Label.
One Unit-level predicate can reference Unit, Space and Target metadata, so there is no need for separate Space or Target filters: the server does the scoping, and only the matching Units come back. These flags are shorthands over --where for the scopes that come up most.
--component scopes by the Space's Component, and the rest by a label on the Space, which is where the standard labels live. That costs something: the server compiles a term naming the Unit's own columns into SQL, but a term prefixed with another entity it cannot, so it expands the Space for every row the query returned and evaluates the term afterwards. The answer is the same either way. Where a command has the choice -- a label the Units carry themselves -- a --where over Labels.* narrows the read and these do not.
func (*QueryFlags) BindStored ¶ added in v0.4.5
func (q *QueryFlags) BindStored(cmd *cobra.Command)
BindStored registers --filter and --contains.
func (*QueryFlags) BindWhere ¶ added in v0.4.5
func (q *QueryFlags) BindWhere(cmd *cobra.Command)
BindWhere registers --where alone, for a command that scopes a query but does not let the caller choose the fields or apply a stored Filter.
func (*QueryFlags) Label ¶ added in v0.4.5
func (q *QueryFlags) Label(flag, spaceLabel, usage string)
Label adds a Space-label scope beyond the standard set, for a label a single command cares about. Its flag joins the predicate with the others, so a command gets its own scoping without every command growing the flag.
Call it before BindSpaceLabels. The terms it adds follow the standard labels; a predicate is a flat conjunction, so their position carries no meaning.
func (QueryFlags) Predicate ¶ added in v0.4.5
func (q QueryFlags) Predicate() (string, error)
Predicate compiles --where and the Space-label shorthands into a single ConfigHub `where` expression, empty when nothing is set (everything the caller can view). A ConfigHub predicate is flat AND-only -- no parentheses, no OR -- so the shorthands are joined to any raw --where with a bare AND.
A label value carrying a quote or a backslash is rejected. The server screens every filter literal with an anchored regexp admitting neither, because it builds its SQL by concatenation with no parameter binding and that screen is what keeps a filter from being an injection point -- so such a value cannot be sent at all, and rewriting it would scope the query to a different value than the operator typed. Rejecting it here names the flag they typed, where the server can only name a position in an expression they never wrote. The API client applies the same rule to the predicates it builds; the rule is restated here because this package does not depend on it.
type TableOptions ¶
type TableOptions struct {
// NoHeader suppresses the header row.
NoHeader bool
// Aliases and CustomColumns are passed to the column provider.
Aliases map[string]string
CustomColumns map[string]func(any) string
}
TableOptions configures RenderTable.