Documentation
¶
Overview ¶
Package cli routes command-line arguments to handlers.
A Mux maps command names to Runner values, similar to how net/http.ServeMux maps URL patterns to net/http.Handler values. A *Command adds typed input declarations (flags, options, and positional arguments) to a Runner. A Program ties a root runner to the process environment and handles signal, I/O, and exit-code normalization.
cmd := &cli.Command{
Run: func(out *cli.Output, call *cli.Call) error {
_, err := fmt.Fprintf(out.Stdout, "hello %s\n", call.Args["name"])
return err
},
}
cmd.Arg("name", "Person to greet")
mux := cli.NewMux("app")
mux.Handle("greet", "Print a greeting", cmd)
Flags are booleans toggled by presence (--verbose). Options carry a string value (--host localhost). Positional arguments are required and ordered. Flags and options must appear before positional arguments; the parser stops at the first non-flag token or after "--".
In POSIX terminology flags and options are both "options." This package separates them because the two forms have different signatures.
Index ¶
- Constants
- Variables
- func DefaultHelpFunc(w io.Writer, help *Help)
- func Flag(name, short string, value bool, usage string)
- func Handle(pattern string, usage string, runner Runner)
- func HandleFunc(pattern string, usage string, fn func(*Output, *Call) error)
- func Option(name, short, value, usage string)
- type Call
- type Command
- type Completer
- type Completion
- type ExitError
- type Flusher
- type Help
- type HelpArg
- type HelpCommand
- type HelpFlag
- type HelpFunc
- type HelpOption
- type Mux
- func (m *Mux) Complete(w io.Writer, tokens []string)
- func (m *Mux) Flag(name, short string, value bool, usage string)
- func (m *Mux) Handle(pattern string, usage string, runner Runner)
- func (m *Mux) HandleFunc(pattern string, usage string, fn func(*Output, *Call) error)
- func (m *Mux) Option(name, short, value, usage string)
- func (m *Mux) RunCLI(out *Output, call *Call) error
- type Output
- type Program
- type Runner
- type RunnerFunc
Examples ¶
Constants ¶
const ( ExitOK = 0 ExitFailure = 1 ExitHelp = 2 )
Standard process exit codes used by Program.Invoke.
Variables ¶
var DefaultProgram = &Program{ Stdout: os.Stdout, Stderr: os.Stderr, Stdin: os.Stdin, LookupEnv: os.LookupEnv, }
DefaultProgram is the default Program used by the package-level Handle, HandleFunc, and Invoke functions.
var ErrHelp = errors.New("cli: help requested")
ErrHelp is returned when help output was displayed instead of executing a command. It is a sentinel distinct from ExitError.
Functions ¶
func DefaultHelpFunc ¶
DefaultHelpFunc is the built-in HelpFunc used when no override is set. It renders a tabular summary to w.
func Flag ¶
Flag declares a boolean flag on the DefaultProgram mux. The mux is created lazily on first use.
func Handle ¶
Handle registers runner on the DefaultProgram mux. The mux is created lazily on first use.
func HandleFunc ¶
HandleFunc registers fn on the DefaultProgram mux. The mux is created lazily on first use.
func Option ¶
func Option(name, short, value, usage string)
Option declares a value option on the DefaultProgram mux. The mux is created lazily on first use.
Types ¶
type Call ¶
type Call struct {
// Pattern is the matched command path (e.g. "app repo init").
Pattern string
// Argv is the remaining argument tail after command routing.
Argv []string
// Stdin is the standard input stream.
Stdin io.Reader
// Env resolves environment variables. It follows the signature of
// [os.LookupEnv].
Env func(string) (string, bool)
// GlobalFlags holds mux-level boolean flags accumulated during routing.
GlobalFlags map[string]bool
// GlobalOptions holds mux-level option values accumulated during routing.
GlobalOptions map[string]string
// Flags holds command-level boolean flags.
Flags map[string]bool
// Options holds command-level option values.
Options map[string]string
// Args holds bound positional arguments.
Args map[string]string
// Rest holds unmatched trailing positional arguments when
// [Command.CaptureRest] is set.
Rest []string
// contains filtered or unexported fields
}
A Call carries the parsed input for a single command invocation. It is the CLI equivalent of an net/http.Request.
func NewCall ¶
NewCall returns a new Call with the given context, pattern, and argv. All map fields are initialized to non-nil empty maps.
It panics if ctx is nil.
func NewCallWithContext ¶
NewCallWithContext returns a shallow copy of call with ctx replacing the original context. Exported maps and slices are deep-copied.
It panics if ctx or call is nil.
Example ¶
package main
import (
"context"
"fmt"
"github.com/mzattahri/cli"
"github.com/mzattahri/cli/clitest"
)
type authContextKey struct{}
func main() {
call := clitest.NewCall("whoami", nil)
ctx := context.WithValue(context.Background(), authContextKey{}, "alice")
derived := cli.NewCallWithContext(ctx, call)
fmt.Print(derived.Context().Value(authContextKey{}))
}
Output: alice
func (*Call) Context ¶
Context returns the call's context, defaulting to context.Background if the call or its context is nil.
type Command ¶
type Command struct {
// Description is the longer help text shown by [HelpFunc].
Description string
// CaptureRest preserves unmatched trailing positional arguments
// in [Call.Rest].
CaptureRest bool
// Run handles the command invocation.
Run RunnerFunc
// contains filtered or unexported fields
}
A Command combines a handler function with per-command input declarations.
Flags, options, and positional arguments are declared with the Command.Flag, Command.Option, and Command.Arg methods. All declarations must be made before the command is registered with Mux.Handle.
Example ¶
package main
import (
"bytes"
"context"
"fmt"
"github.com/mzattahri/cli"
)
func main() {
cmd := &cli.Command{
CaptureRest: true,
Run: func(out *cli.Output, call *cli.Call) error {
detach := call.Flags["detach"]
_, err := fmt.Fprintf(out.Stdout, "image=%s detach=%t command=%v", call.Args["image"], detach, call.Rest)
return err
},
}
cmd.Flag("detach", "", false, "Run in background")
cmd.Arg("image", "Image reference")
mux := cli.NewMux("app")
mux.Handle("run", "Run a container", cmd)
var stdout bytes.Buffer
var stderr bytes.Buffer
program := &cli.Program{Stdout: &stdout, Stderr: &stderr, Runner: mux}
_ = program.Invoke(context.Background(), []string{"app", "run", "--detach", "alpine", "sh", "-c", "echo hi"})
fmt.Print(stdout.String())
}
Output: image=alpine detach=true command=[sh -c echo hi]
func (*Command) Arg ¶
Arg declares a required positional argument. It panics if name is empty or duplicated.
func (*Command) Complete ¶
Complete writes tab-completion candidates for command-level flags and options to w, implementing the Completer interface.
func (*Command) Flag ¶
Flag declares a boolean flag toggled by presence.
short is an optional one-character short form (e.g. "v" for -v). An empty string means the flag has no short form. It panics on duplicate or reserved names.
type Completer ¶
A Completer writes tab completions for a partial command line to w. The tokens parameter contains the tokens currently on the command line; the last element is the partial word being typed (possibly empty). Each completion is written as a tab-separated line: value\tdescription.
*Mux and *Command both implement Completer. A Mux completes subcommands and mux-level flags; a Command completes command-level flags and options.
type Completion ¶
A Completion is a single tab-completion candidate.
type ExitError ¶
An ExitError is an error with an explicit process exit code.
func Invoke ¶
Invoke calls DefaultProgram.Invoke(ctx, args).
type Flusher ¶
type Flusher interface {
Flush() error
}
Flusher is implemented by writers that support flushing buffered output.
type Help ¶
type Help struct {
// Name is the final segment of the command path.
Name string
// FullPath is the complete command path (e.g. "app repo init").
FullPath string
// Usage is a short one-line summary.
Usage string
// Description is longer free-form help text.
Description string
// GlobalFlags lists program-level boolean flags in scope.
GlobalFlags []HelpFlag
// GlobalOptions lists program-level value options in scope.
GlobalOptions []HelpOption
// Commands lists the immediate child commands.
Commands []HelpCommand
// Arguments lists positional arguments accepted by this command.
Arguments []HelpArg
// Flags lists command-level boolean flags.
Flags []HelpFlag
// Options lists command-level value options.
Options []HelpOption
}
Help holds the data passed to a HelpFunc when rendering help output.
type HelpCommand ¶
A HelpCommand describes an immediate child command in rendered help.
type HelpOption ¶
A HelpOption describes a value option in rendered help.
type Mux ¶
type Mux struct {
Name string
// contains filtered or unexported fields
}
A Mux is a command multiplexer. It routes argv tokens to Runner values registered with Mux.Handle, in the same spirit as net/http.ServeMux.
func (*Mux) Complete ¶
Complete writes tab-completion candidates for the given command line tokens to w, implementing the Completer interface.
The trie walk matches completed tokens against registered subcommands. When a child node implements Completer (a mounted *Mux or a *Command), the remaining tokens are delegated to that node's Complete method. Otherwise, the mux completes its own subcommands and mux-level flags.
func (*Mux) Flag ¶
Flag declares a mux-level boolean flag that is parsed before subcommand routing. Parsed values accumulate in [Call.GlobalFlags].
short is an optional one-character short form (e.g. "v" for -v). An empty string means the flag has no short form. It panics on duplicate or reserved names.
Example ¶
package main
import (
"bytes"
"context"
"fmt"
"github.com/mzattahri/cli"
)
func main() {
mux := cli.NewMux("app")
mux.Flag("verbose", "v", false, "Enable verbose output")
mux.Handle("status", "Print status", cli.RunnerFunc(func(out *cli.Output, call *cli.Call) error {
_, err := fmt.Fprintf(out.Stdout, "verbose=%t", call.GlobalFlags["verbose"])
return err
}))
var stdout bytes.Buffer
program := &cli.Program{Stdout: &stdout, Stderr: &bytes.Buffer{}, Runner: mux}
_ = program.Invoke(context.Background(), []string{"app", "--verbose", "status"})
fmt.Print(stdout.String())
}
Output: verbose=true
func (*Mux) Handle ¶
Handle registers runner for the given command pattern with a short usage summary shown in help output.
Pattern segments are split on whitespace. Multi-segment patterns create nested command paths (e.g. "repo init"). If runner is a *Mux, it is mounted as a sub-mux at pattern. It panics on conflicting registrations or a nil runner.
Example (OptionsOnly) ¶
package main
import (
"bytes"
"context"
"fmt"
"github.com/mzattahri/cli"
)
func main() {
cmd := &cli.Command{
Run: func(out *cli.Output, call *cli.Call) error {
host := call.Options["host"]
_, err := fmt.Fprint(out.Stdout, host)
return err
},
}
cmd.Option("host", "", "", "daemon socket")
mux := cli.NewMux("app")
mux.Handle("status", "Print status", cmd)
var stdout bytes.Buffer
var stderr bytes.Buffer
program := &cli.Program{Stdout: &stdout, Stderr: &stderr, Runner: mux}
_ = program.Invoke(context.Background(), []string{"app", "status", "--host", "unix:///tmp/docker.sock"})
fmt.Print(stdout.String())
}
Output: unix:///tmp/docker.sock
Example (Subtree) ¶
package main
import (
"bytes"
"context"
"fmt"
"github.com/mzattahri/cli"
)
func main() {
repo := cli.NewMux("repo")
repo.Handle("init", "Initialize a repository", cli.RunnerFunc(func(out *cli.Output, call *cli.Call) error {
_, err := fmt.Fprint(out.Stdout, "initialized")
return err
}))
mux := cli.NewMux("app")
mux.Handle("repo", "Manage repositories", repo)
var stdout bytes.Buffer
var stderr bytes.Buffer
program := &cli.Program{Stdout: &stdout, Stderr: &stderr, Runner: mux}
_ = program.Invoke(context.Background(), []string{"app", "repo", "init"})
fmt.Print(stdout.String())
}
Output: initialized
func (*Mux) HandleFunc ¶
HandleFunc registers fn as the handler for pattern. It is a shorthand for Handle(pattern, usage, RunnerFunc(fn)).
Example ¶
package main
import (
"bytes"
"context"
"fmt"
"github.com/mzattahri/cli"
)
func main() {
mux := cli.NewMux("app")
mux.HandleFunc("version", "Print version", func(out *cli.Output, call *cli.Call) error {
_, err := fmt.Fprint(out.Stdout, "v1.0.0")
return err
})
var stdout bytes.Buffer
var stderr bytes.Buffer
program := &cli.Program{Stdout: &stdout, Stderr: &stderr, Runner: mux}
_ = program.Invoke(context.Background(), []string{"app", "version"})
fmt.Print(stdout.String())
}
Output: v1.0.0
func (*Mux) Option ¶
Option declares a mux-level named value option that is parsed before subcommand routing. Parsed values accumulate in [Call.GlobalOptions].
short is an optional one-character short form (e.g. "c" for -c). An empty string means the option has no short form. It panics on duplicate or reserved names.
type Output ¶
type Output struct {
// Stdout is the standard output stream. If it implements [Flusher],
// [Output.Flush] flushes it after the runner returns.
Stdout io.Writer
// Stderr is the standard error stream. If it implements [Flusher],
// [Output.Flush] flushes it after the runner returns.
Stderr io.Writer
}
Output carries the output streams for a command invocation.
type Program ¶
type Program struct {
// Name is shown in usage output. When empty, [Program.Invoke]
// uses args[0].
Name string
// Runner is the root runner. When nil on [DefaultProgram], a
// default [Mux] is created lazily.
Runner Runner
// Stdout is the standard output writer. When nil, [Program.Invoke]
// uses [os.Stdout].
Stdout io.Writer
// Stderr is the standard error writer. When nil, [Program.Invoke]
// uses [os.Stderr].
Stderr io.Writer
// Stdin is the standard input reader. When nil, [Program.Invoke]
// uses [os.Stdin].
Stdin io.Reader
// LookupEnv resolves environment variables. When nil,
// [Program.Invoke] uses [os.LookupEnv].
LookupEnv func(string) (string, bool)
// Usage is the short summary shown in top-level help output.
Usage string
// Description is longer free-form help text.
Description string
// HelpFunc overrides the default help renderer.
HelpFunc HelpFunc
// IgnoreSignals disables the default SIGINT context wrapping.
IgnoreSignals bool
}
A Program describes the process-level invocation environment for a root Runner.
func (*Program) Flag ¶
Flag declares a boolean flag on the underlying *Mux runner. It panics if the program's Runner is not a *Mux. See Mux.Flag for details.
func (*Program) Invoke ¶
Invoke runs the program's root Runner and normalizes the result to an *ExitError. An explicit --help request returns nil after rendering help. It panics if ctx is nil.
Example ¶
package main
import (
"bytes"
"context"
"fmt"
"github.com/mzattahri/cli"
)
func main() {
cmd := &cli.Command{
Run: func(out *cli.Output, call *cli.Call) error {
_, err := fmt.Fprintf(out.Stdout, "hello %s", call.Args["name"])
return err
},
}
cmd.Arg("name", "Person to greet")
mux := cli.NewMux("app")
mux.Handle("greet", "Print a greeting", cmd)
var stdout bytes.Buffer
var stderr bytes.Buffer
program := &cli.Program{
Stdout: &stdout,
Stderr: &stderr,
Runner: mux,
}
_ = program.Invoke(context.Background(), []string{"app", "greet", "gopher"})
fmt.Print(stdout.String())
}
Output: hello gopher
type Runner ¶
A Runner executes a CLI command. It receives an *Output for writing and a *Call carrying the parsed input.
func CompletionRunner ¶
CompletionRunner returns a Runner that outputs tab completions for the current command line.
Shell integration scripts invoke this runner on each TAB press, passing the current tokens (shell tokens) as positional arguments after "--":
myapp complete -- repo init --f
It panics if c is nil.
Example ¶
package main
import (
"bytes"
"context"
"fmt"
"github.com/mzattahri/cli"
)
func main() {
mux := cli.NewMux("app")
mux.Handle("greet", "Print a greeting", cli.RunnerFunc(func(out *cli.Output, call *cli.Call) error {
_, err := fmt.Fprint(out.Stdout, "hello")
return err
}))
mux.Handle("complete", "Output completions", cli.CompletionRunner(mux))
var stdout bytes.Buffer
var stderr bytes.Buffer
program := &cli.Program{Stdout: &stdout, Stderr: &stderr, Runner: mux}
_ = program.Invoke(context.Background(), []string{"app", "complete", "--", "gr"})
fmt.Print(stdout.String())
}
Output: greet Print a greeting