Documentation ¶
Overview ¶
Package clif provides a framework for writing command line applications.
The framework starts with an Application, which defines some global flags that apply to all commands and the different Commands that the application accepts.
Each flag is defined by a FlagDef, which describes the flag name and how to parse it.
Each Command describes the command name, any subcommands and flags it accepts, and other information about parsing the command and how to execute it.
Once input is matched to the Command, it calls the HandlerBuilder associated with that Command. The HandlerBuilder is responsible for turning flags, arguments, and a Command into a Handler. It's separated out from the Handler so the business logic of the Handler can be separated out from the logic to parse the flags and arguments.
Finally, once we have a Handler, it gets executed, with a Response to write output to and record the desired exit code of the command.
Index ¶
- func FlagsHelp(command parseable) string
- func SubcommandsHelp(command parseable) string
- type Application
- type Command
- type DuplicateFlagNameError
- type ExtraInputError
- type Flag
- type FlagDef
- type FlagParser
- type Handler
- type HandlerBuilder
- type MissingFlagValueError
- type Response
- type RouteResult
- type RunOption
- type RunOptions
- type UnexpectedCommandArgError
- type UnexpectedFlagValueError
- type UnknownFlagNameError
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func FlagsHelp ¶
func FlagsHelp(command parseable) string
FlagsHelp returns a default usage string for the flags defined for the passed Command or Application.
func SubcommandsHelp ¶
func SubcommandsHelp(command parseable) string
SubcommandsHelp returns a default usage string for the subcommands provided by the passed Command or Application.
Types ¶
type Application ¶
type Application struct { // Commands are the commands that the application supports. Commands []Command // Flags are the definitions for any global flags the application // supports. Flags []FlagDef }
Application is the root definition of a CLI.
Example ¶
package main import ( "context" "fmt" "impractical.co/clif" "impractical.co/clif/flagtypes" ) type funcCommandHandler func(ctx context.Context, resp *clif.Response) func (f funcCommandHandler) Build(_ context.Context, _ map[string]clif.Flag, _ []string, _ *clif.Response) clif.Handler { //nolint:ireturn // filling an interface return f } func (f funcCommandHandler) Handle(ctx context.Context, resp *clif.Response) { f(ctx, resp) } type flagCommandHandler struct { flags map[string]clif.Flag args []string f func(ctx context.Context, flags map[string]clif.Flag, args []string, resp *clif.Response) } func (f flagCommandHandler) Build(_ context.Context, flags map[string]clif.Flag, args []string, _ *clif.Response) clif.Handler { //nolint:ireturn // filling an interface f.flags = flags f.args = args return f } func (f flagCommandHandler) Handle(ctx context.Context, resp *clif.Response) { f.f(ctx, f.flags, f.args, resp) } //nolint:errcheck // several places we're not checking errors because they can't fail func main() { app := clif.Application{ Commands: []clif.Command{ { Name: "help", Description: "Displays help information about this program.", Handler: funcCommandHandler(func(_ context.Context, resp *clif.Response) { resp.Output.Write([]byte("this is help information\n")) }), }, { Name: "foo", Subcommands: []clif.Command{ { Name: "bar", Flags: []clif.FlagDef{ { Name: "quux", ValueAccepted: true, OnlyAfterCommandName: false, Parser: flagtypes.StringParser{}, }, }, Handler: flagCommandHandler{ f: func(_ context.Context, flags map[string]clif.Flag, args []string, resp *clif.Response) { fmt.Fprintln(resp.Output, flags, args) }, }, }, }, }, }, Flags: []clif.FlagDef{ { Name: "baaz", Parser: flagtypes.BoolParser{}, }, }, } res := app.Run(context.Background(), clif.WithArgs([]string{"help"})) fmt.Println(res) res = app.Run(context.Background(), clif.WithArgs([]string{"foo", "bar", "--quux", "hello"})) fmt.Println(res) res = app.Run(context.Background(), clif.WithArgs([]string{"foo", "--quux", "hello", "bar"})) fmt.Println(res) res = app.Run(context.Background(), clif.WithArgs([]string{"--quux", "hello", "foo", "bar"})) fmt.Println(res) res = app.Run(context.Background(), clif.WithArgs([]string{"foo", "bar", "--quux=hello"})) fmt.Println(res) res = app.Run(context.Background(), clif.WithArgs([]string{"foo", "--quux=hello", "bar"})) fmt.Println(res) res = app.Run(context.Background(), clif.WithArgs([]string{"--quux=hello", "foo", "bar"})) fmt.Println(res) res = app.Run(context.Background(), clif.WithArgs([]string{"--baaz", "foo", "bar", "--quux", "hello"})) fmt.Println(res) }
Output: this is help information 0 map[quux:{quux hello hello}] [] 0 map[quux:{quux hello hello}] [] 0 map[quux:{quux hello hello}] [] 0 map[quux:{quux hello hello}] [] 0 map[quux:{quux hello hello}] [] 0 map[quux:{quux hello hello}] [] 0 map[baaz:{baaz true} quux:{quux hello hello}] [] 0
func (Application) Run ¶
func (app Application) Run(ctx context.Context, opts ...RunOption) int
Run executes the invoked command. It routes the input to the appropriate Command, parses it with the HandlerBuilder, and executes the Handler. The return is the status code the command has indicated it exited with.
type Command ¶
type Command struct { // Name is the name of the command, what the user will type to prompt // its functionality. Name string // Aliases are acceptable variations on Name; they will be treated as // equivalent to Name, but will not be listed in the SubcommandsHelp // output. Aliases []string // Description is a short, one-line description of the command, used // when generating the SubcommandsHelp output. Description string // Hidden indicates whether a command should be included in // SubcommandsHelp output or not. If set to true, the command will be // omitted from SubcommandsHelp output. Hidden bool // Flags holds definitions for the flags, if any, that this command // accepts. Flags []FlagDef // Subcommands are the various subcommands, if any, that this command // accepts. Subcommands []Command // Handler is the HandlerBuilder executed when this Command is used. // The Handler will not be executed if a subcommand of this Command is // used. Handler HandlerBuilder // ArgsAccepted indicates whether free input is expected as part of // this command, separate from flag values and subcommands. ArgsAccepted bool // AllowNonFlagFlags controls whether things that aren't flags (like // flag values, subcommands, and arguments) can start with --. If // false, we'll throw an error when we encounter an -- that doesn't // have a FlagDef for it on this command or any of its subcommands. If // true, we'll allow it, using it either as a flag value, subcommand, // or argument, whichever is allowed. If none are allowed, it will // still throw an invalid flag error. AllowNonFlagFlags bool }
Command defines a command the user can run. Commands can have handlers, that get invoked when the command is run, and subcommands, which are other commands namespaced under their command. Commands with subcommands can still be invoked, and should still have a handler defined, even if it just prints out usage information on the subcommands.
type DuplicateFlagNameError ¶
type DuplicateFlagNameError string
DuplicateFlagNameError is returned when multiple Commands use the same flag name, and it would be ambiguous which Command the flag applies to. The underlying string will be the flag name, without leading --.
func (DuplicateFlagNameError) Error ¶
func (err DuplicateFlagNameError) Error() string
type ExtraInputError ¶
type ExtraInputError struct { // Application is the Application that produced the error. Application Application // CommandPath is the Commands, in order, that were matched before the // error was produced. Each Command in the slice is the child of the // Command before it in the slice. CommandPath []Command // Flags holds the Flags that were matched before the error was // produced. Flags map[string]Flag // Args holds the positional arguments that were parsed before the // error was produced. Args []string // ExtraInput holds the extra, unexpected input. ExtraInput []string }
ExtraInputError is returned when a command gets more input than it knows what to do with.
func (ExtraInputError) Error ¶
func (err ExtraInputError) Error() string
type Flag ¶
Flag is an interface that holds information about a flag at runtime. Applications will almost always want to type assert this to the flag type returned by the FlagParser in the FlagDef for that specific flag, to get a parsed version of the value.
type FlagDef ¶
type FlagDef struct { // Name is the name of the flag. It's what will be surfaced in // documentation and what the user will use when applying the flag to a // command. Names must be unique across all commands, or the parser // won't know which command to apply the flag to. Name string // Aliases holds any alternative names the flag should accept from the // user. Aliases are not surfaced in documentation, by default. Aliases // must be unique across all other aliases and names for all commands, // or the parser won't know which command to apply the flag to. Aliases []string // Description is a user-friendly description of what the flag does and // what it's for, to be presented as part of help output. Description string // ValueAccepted indicates whether or not the flag should allow a // value. If set to false, attempting to pass a value will surface an // error. ValueAccepted bool // OnlyAfterCommandName indicates whether the flag should only be // acceptable after the command name in the invocation, or if can // appear anywhere in the invocation. If set to true, passing the flag // before the subcommand it belongs to will return an error. OnlyAfterCommandName bool // Parser determines how the flag value should be parsed. Parser FlagParser }
FlagDef holds the definition of a flag.
type FlagParser ¶
type FlagParser interface { // Parse is called to turn a name and value string into a Flag. The // prior value is passed to support flags that can be passed multiple // times to specify multiple values. Parse(ctx context.Context, name, value string, prior Flag) (Flag, error) // FlagType should return a user-friendly indication of the type of // input this flag type expects, like "string" or "int" or "timestamp". FlagType() string }
FlagParser is an interface for parsing flag values. Implementing it allows the definition of new types of flags.
type Handler ¶
type Handler interface { // Handle is a method that will be called when the command is executed. // It should contain the business logic of the command. Handle(ctx context.Context, resp *Response) }
Handler is an interface that commands should implement. The implementing type should probably be a struct, with arguments and dependencies defined as fields on the struct.
type HandlerBuilder ¶
type HandlerBuilder interface { // Build creates a Handler by parsing the Flags and args into the // appropriate handler type. Build(ctx context.Context, flags map[string]Flag, args []string, resp *Response) Handler }
HandlerBuilder is an interface that should wrap a Handler. It parses the passed Flags and args into a Handler, to separate out the parsing logic from the business logic.
type MissingFlagValueError ¶
type MissingFlagValueError string
MissingFlagValueError is returned when a flag was used without a value, but the flag requires a value. The underlying string is the name of the flag, without leading --.
func (MissingFlagValueError) Error ¶
func (err MissingFlagValueError) Error() string
type Response ¶
type Response struct { // Code is the status code the command will exit with. Code int // Output is the writer that should be used for command output. It will // usually be set to the shell's standard output. Output io.Writer // Error is the writer that should be used to communicate error // conditions. It will usually be set to the shell's standard error. Error io.Writer }
Response holds the ways a command can present information to the user.
type RouteResult ¶
type RouteResult struct { // Command is the Command that Route believes should be run. Command Command // Flags are the Flags that should be applied to that command. Flags map[string]Flag // Args are the positional arguments that should be passed to that // command. Args []string }
RouteResult holds information about the Command that should be run and the Flags and arguments to pass to it, based on the parsing done by Route.
func Route ¶
func Route(ctx context.Context, root Application, input []string) (RouteResult, error)
Route parses the passed input in the context of the passed Application, turning it into a Command with Flags and arguments.
type RunOption ¶
type RunOption func(*RunOptions)
RunOption is a function type that modifies a passed RunOptions when called. It's used to configure the behavior of Application.Run.
func WithArgs ¶
WithArgs is a RunOption that sets the arguments that will be parsed as the command's input to the passed strings.
type RunOptions ¶
type RunOptions struct { // Output is where command output should be written. Defaults to // os.Stdout. Output io.Writer // Error is where the command should write information about errors. // Defaults to os.Stderr. Error io.Writer // Args are the arguments that were passed to the command. Defaults // to os.Args[1:]. Args []string }
RunOptions holds all the options to pass to Application.Run. It should be built by using RunOption functions to modify a passed in RunOptions.
type UnexpectedCommandArgError ¶
type UnexpectedCommandArgError string
UnexpectedCommandArgError is returned when a command that wasn't expecting an argument gets one.
func (UnexpectedCommandArgError) Error ¶
func (err UnexpectedCommandArgError) Error() string
type UnexpectedFlagValueError ¶
type UnexpectedFlagValueError struct { // Flag is the flag name, without leading --. Flag string // Value is the value that was passed to the flag. Value string }
UnexpectedFlagValueError is returned when a flag was used with a value, but the flag doesn't accept values.
func (UnexpectedFlagValueError) Error ¶
func (err UnexpectedFlagValueError) Error() string
type UnknownFlagNameError ¶
type UnknownFlagNameError string
UnknownFlagNameError is returned when an argument uses flag syntax, starting with a --, but doesn't match a flag configured for that Command. The underlying string will be the flag name, without leading --.
func (UnknownFlagNameError) Error ¶
func (err UnknownFlagNameError) Error() string