Documentation
¶
Overview ¶
Package tool is the one shape of a nova command. A Tool is its verbs; a Verb declares its flags and returns one value, Out, which is rendered either as typed lines or as the JSON of the same value (out.go). Everything a command writes for itself lives here once: the verb dispatch, the banner (what the tool is, how it works, its usage lines, its exit codes, a runnable example block), `help` and `<verb> -h`, the `version` verb, the standard flags (--json on every verb; --max and --dry-run where a verb opts in), refusing to guess (every problem of one invocation named at once), and the refusal line with its remedy: an unknown verb or flag is answered with the nearest name and the ones there are, and a verb group's -h lists its verbs. A row carries a prose tail (Out.ItemText): a reason or a command renders plain after the row's typed fields and as the `text` field of the row's JSON, so the line and the object stay one value. A tool whose exit 0 already means CLEAR sets HelpRefused, and `<verb> -h` is then a refusal at exit 2 naming `help`, never an answer at exit 0. A tool may name a default verb (`<tool> <file>`) and its own status words (STALE beside FAIL). A verb may be hidden (Verb.Hidden): it runs and answers `-h`, and the banner, the usage block and the unknown-verb list do not show it, a probe step verb a user never types. A long-running verb prints each item as it goes; the Out it returns is the closing line. Call.Ctx is cancelled when the run's context ends and on interrupt (RunContext). A command holds only what its verbs do.
Index ¶
- Constants
- type Call
- func (c *Call) Bool(name string) bool
- func (c *Call) DryRun() bool
- func (c *Call) Dur(name string) time.Duration
- func (c *Call) Get(name string) any
- func (c *Call) Given(name string) bool
- func (c *Call) Int(name string) int
- func (c *Call) Problem(what string)
- func (c *Call) ProblemAs(reason, what string)
- func (c *Call) Refused() *Out
- func (c *Call) Str(name string) string
- func (c *Call) Want(name, wants string) string
- type Effect
- type Field
- type Fields
- type Flags
- type Item
- type More
- type Out
- func (o *Out) As(word string) *Out
- func (o *Out) Cap(max int) *Out
- func (o *Out) Fact(k string, v any) *Out
- func (o *Out) Findings(kinds ...string) *Out
- func (o *Out) Item(kind string, kv ...any) *Out
- func (o *Out) ItemText(kind, text string, kv ...any) *Out
- func (o *Out) MarshalJSON() ([]byte, error)
- func (o *Out) Note(text string) *Out
- func (o *Out) Render(w io.Writer, asJSON bool) int
- type Status
- type Text
- type Tool
- type Topic
- type Verb
Constants ¶
const ( HowLines = 5 HowWidth = 100 )
HowLines and HowWidth bound the banner's how-it-works text: a reader takes in five lines at a glance, and a line past 100 characters wraps.
const HowLabel = "how it works: "
HowLabel opens the how-it-works text in the banner (docs/ONBOARDING.md point 6), so a tool's How is the paragraph without it; the width bound counts it on the first line, where it is printed.
const MaxRemedy = "--max <n> raises the ceiling, --max 0 lists all"
MaxRemedy is the remedy every MORE line names.
const MaxWords = 6
MaxWords bounds a tool's own status words: a reader learns them all at once.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Call ¶
type Call struct {
// Ctx is the run's context, cancelled when that context ends and on
// interrupt (skeleton contract 2.4). A verb that runs for a while selects
// on it; a context that has ended is the closing line, not the verb's Out.
Ctx context.Context
Stdin io.Reader
Stdout, Stderr io.Writer // written by a verb that Prints
// contains filtered or unexported fields
}
Call is one invocation of a verb: its streams, its context and its parsed flags.
func (*Call) DryRun ¶
DryRun reports whether --dry-run was given (only a Verb with DryRun takes it). A verb reads it before it writes and, when it is set, returns the plan the real run would carry out, from the same code path, and writes nothing; the skeleton adds dry_run=true to the OK line unless the verb set it.
func (*Call) Get ¶
Get is a declared flag's value, for a flag.Value of the verb's own (a flag.Getter).
func (*Call) ProblemAs ¶
ProblemAs records one reason with a stable code (skeleton contract 2.10, STANDARD §2). The refusal line carries reason=<code> before the colon, and the JSON result adds "reasons" beside "why": a program reads the code, a person reads the sentence.
func (*Call) Refused ¶
Refused is the refusal naming every problem recorded, or nil when there is none.
type Effect ¶
type Effect string
Effect is what running a verb does beyond printing: one of the three below, optionally with a clause after it; a verb whose flags change it states the strongest and says which flag (Problems holds every verb to one of the three).
type Fields ¶
type Fields []Field
Fields keeps its keys in the order they were added, in both renderings.
func (Fields) MarshalJSON ¶
MarshalJSON writes the fields as one object in the order they were added.
type Flags ¶
Flags is one verb's flag set: package flag's own, with its two mouths closed (verbflag.New), and the standard flags a verb opts into.
func (*Flags) Check ¶
Check adds a rule over the parsed flags (c.Problem, c.Want), run with the required flags, --max and the arguments before the verb runs.
func (*Flags) Max ¶
func (f *Flags) Max()
Max adds --max: the items listed before one MORE line stands for the rest.
type Item ¶
type Item struct {
Kind string `json:"kind"`
Fields Fields `json:"fields"`
Text string `json:"text,omitempty"`
}
Item is one typed row: `<TOKEN> <KIND> k=v ...[: <text>]`, where the text is the row's prose tail (ItemText): a reason or a command, printed plain after the typed fields and carried as the `text` field of the JSON, so the line and the object stay one value.
type More ¶
type More struct {
Kind string `json:"kind"`
Shown int `json:"shown"`
Total int `json:"total"`
Remedy string `json:"remedy"`
}
More stands for the items of one kind that --max did not list.
type Out ¶
type Out struct {
Verb string
Status Status
Exit int
Word string // the tool's own status word in place of OK or FAILED (Out.As); "" is the plain one
Remedy string // what to run next: the tool's help on a refusal unless the verb names better
Why []string // every reason it failed or was refused
Reasons []string // a stable code per Why, parallel to it; a program reads the code (skeleton contract 2.10)
Facts Fields
Items []Item
More []More
Notes []string
Payload string
// contains filtered or unexported fields
}
Out is the one value every verb returns. Render writes it as typed lines:
<TOKEN> OK|FAILED|REFUSED [reason=<r>] k=v ...[: <why>][; run: <remedy>] one line per why <TOKEN> <KIND> k=v ...[: <text>] one line per item, the text its prose tail <TOKEN> MORE kind=<kind> shown=<n> total=<n> <remedy> one per capped kind <TOKEN> NOTE <text> one per note
or as the JSON of the same value:
{"result":{"verb","status","exit","remedy","why","reasons"},"facts":{},"items":[{"kind","fields","text"}],
"more":[{"kind","shown","total","remedy"}],"notes":[],"payload":""}
A payload (the version line, a document a program reads) is printed as it is, last, in the text form, and alone when the result carries nothing else; it is the "payload" key of the JSON. Values are strings, integers, booleans or Text; an empty value is "-" in the text form. The JSON is not HTML-escaped: `<dir>` is `<dir>` in both renderings.
func Done ¶
func Done() *Out
Done is an OK result, Refuse one that could not run, Fail one that ran and said no.
func (*Out) As ¶
As puts one of the tool's own status words (Tool.Words) in place of OK or FAILED on the first line; the status and the exit stay: `Done().As("UNCHANGED")` exits 0, and a gate that says no is `Fail(why).As("STALE")`, exit 1, apart from a refusal's 2. A word the tool does not declare, or one on a refusal, is turned into a FAILED naming the bug.
func (*Out) Cap ¶
Cap keeps the first max items of each kind (0 keeps all) and records a More for each kind with items left over, counted by pkg/bounded: the `MORE kind= shown= total=` cut. A verb with Flags.Max is capped by the skeleton; a verb that bounds a listing of its own calls Cap.
func (*Out) Findings ¶
Findings names the item kinds that are a verb's findings: in the text form their lines go to stderr, and the rest of a verb that ran (its first line, its other items, MORE and NOTE) to stdout, whether it said OK or FAILED. A refusal stays whole on stderr; JSON stays one object on stdout.
func (*Out) ItemText ¶
ItemText adds one row of a kind with a prose tail: the typed fields render as key=value tokens and the text renders plain after them, `: <text>` (oneline.Escape), while the JSON carries it as the `text` field beside the typed fields (STANDARD §2, one output structure, two renderings). A reason or a command lives in the tail, never escaped into one key=value field.
func (*Out) MarshalJSON ¶
MarshalJSON is the JSON rendering: the result, then the value's parts.
func (*Out) Render ¶
Render writes o as typed lines, or as one JSON object when json is set, and returns the exit that stands: o.Exit, or 1 when o is no JSON (a NaN or an infinite float, a value of the verb's own that JSON cannot carry), which is then a FAILED line naming the verb, never an empty line and a success. Every value goes through pkg/oneline.
type Status ¶
type Status string
Status is how a verb ended: ok (exit 0), failed (it ran and said no, exit 1) or refused (it could not run, exit 2).
type Text ¶
type Text string
Text is a value of free text, a reason, a sentence or a command to run, as a fact or an item's field: the text form prints it after the line's typed fields, its prose tail, quoted (oneline.Quote) so it keeps its spaces and is not hex-escaped, where a typed value (a name, a path, a count) is one token (oneline.Field): `REPORT UNKNOWN name=x path=p reason="no version line" run="nova-version report -h"`. JSON carries it as a string under its key.
type Tool ¶
type Tool struct {
Name string // the binary: nova-<name>
What string // line 1 of the banner: what the tool is for
// Stage, when set, is one sentence on how ready the tool is ("nova-x is
// pre-alpha: not ready for production use."): the banner's line 2, the
// second line of every verb's -h, and an indented NOTE line under a bare
// command's refusal, so no reader meets the tool without it.
Stage string
How string // how it works: the paragraph under line 1
Verbs []Verb // in banner order; version and help are added here
ExitTable string // "0 ..., 1 ..., 2 ...": the banner's exit-codes line
Stamp string // the build stamp (-ldflags -X main.version), for version
// Default is the verb run when the first word is no verb: a flag, a path
// (a word with a separator), or a word naming a file that is there
// (`<tool> <file>...`); any other word is refused as no verb and no file.
// The default verb accepts positional arguments, also when named explicitly.
// "" makes every first word a verb and leaves every verb flags-only.
Default string
// Exists reports whether path is a file or directory that is there: the
// one seam the skeleton reads the filesystem through (skeleton contract
// 2.1). Nil defaults to checking with os.Stat. Tests pass a map.
Exists func(path string) bool
// Words are the tool's own status words (STALE, MISSING, UNCHANGED), the
// only ones Out.As may put in place of OK or FAILED: at most MaxWords,
// upper case, none of OK, FAILED, REFUSED, MORE or NOTE (Problems).
Words []string
// HelpRefused refuses `<verb> -h` (and --help) at exit 2 instead of
// answering it at exit 0: a tool sets it when its exit 0 already means
// CLEAR, so a `-h` answer could read as CLEAR (STANDARD §3 names the one
// exception). The refusal names `help` as the door. No tool sets it yet.
HelpRefused bool
// NoJSON, when set, replaces the banner's standard --json sentence's
// clause after the colon: a tool whose verbs print their own prose (the
// note body a bus carries) says there why they take no --json and pastes
// a line they print, so the help and the output cannot drift (ONBOARDING
// point 6). Empty keeps the standard sentence.
NoJSON string
// UsageNote, when set, is printed under the usage block, before the
// standard flags sentence: a tool states once, where a reader has just
// read the usage lines, the shape of a value several of them name (the
// manifest of --file), so no usage line carries it. Empty prints none.
UsageNote string
// Topics are the tool's help topics: `help <topic>` prints the topic's
// text at exit 0, and the banner lists the topic names on one line
// (skeleton contract 2.7). A tool's reference text lives here, never in
// the banner, which a reader takes in at a glance (STANDARD §3 point 6).
// A topic's name is none of the tool's verbs, since `help <name>` is one
// door (Problems).
Topics []Topic
}
Tool is one command.
func (*Tool) Banner ¶
Banner is what `help` prints: what, how, usage, the standard flags, the exit codes, and the example block last (docs/ONBOARDING.md point 1).
func (*Tool) Main ¶
Main runs the tool over the process's arguments and streams and returns the exit code.
func (*Tool) Problems ¶
Problems is where t falls short of the standard its banner and help carry by construction only when the definition is complete: a what line, an exit table, a how text of at most HowLines lines of at most HowWidth characters, and every verb's effect one of inspection, local write or delivery. A tool on this package runs it in its own tests (internal/ci holds every such package to that).
func (*Tool) Run ¶
Run dispatches one invocation: help, version, or a verb. `<verb> -h` and `help <verb>` print that verb's help on stdout at exit 0 before anything is read or written (the CLI style's rule (b)); a tool that refuses help (HelpRefused) still answers `help <verb>` by name, while `<verb> -h` is a refusal at exit 2, since its exit 0 would read as CLEAR.
func (*Tool) RunContext ¶
func (t *Tool) RunContext(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.Writer) int
RunContext is Run on ctx. Call.Ctx is ctx, cancelled also on interrupt, so a long-running verb can stop (skeleton contract 2.4, STANDARD §2). A context that has already ended does not run the verb: the closing line names the context's error. One cancelled while the verb runs replaces that closing line the same way.
type Topic ¶
Topic is one help topic: `<tool> help <name>` prints Text on stdout at exit 0. It is where a tool's reference text lives, so the banner stays short (skeleton contract 2.7, STANDARD §3 point 6).
type Verb ¶
type Verb struct {
Name string
Usage string // the usage line(s) after the tool's name, one form per line
Example string // runnable line(s) after the tool's name, for the banner's example block
Effect Effect // what running it does to the world, stated in `help <verb>`
Detail string // lines `help <verb>` prints above its flags: a format, a worked example
ExitTable string // this verb's exit codes, quoted by its -h; "" quotes the tool's
DryRun bool // the verb takes --dry-run and honours it (Call.DryRun): it plans and writes nothing
Hidden bool // the verb runs and answers -h and `help <it>`, but the banner, the usage block and the verb lists a refusal names do not show it: a probe step verb a user never types (STANDARD §3, help is never a refusal; §2, a list names the verbs there are for the reader)
Flags func(f *Flags) // declares the verb's flags; nil declares none
Run func(c *Call) *Out
}
Verb is one verb of a tool. A name of two words ("fn load") puts the verb in a group ("fn"): `<tool> fn -h` lists the group's verbs at exit 0.