Documentation
¶
Overview ¶
Package subcmd provides types and functions for creating command-line interfaces with subcommands and flags.
Example ¶
package main import ( "context" "database/sql" "flag" "github.com/bobg/subcmd/v2" ) func main() { // First, parse global flags normally. var ( driver = flag.String("driver", "sqlite3", "database driver") dbname = flag.String("db", "", "database name") ) flag.Parse() db, err := sql.Open(*driver, *dbname) if err != nil { panic(err) } // Stash the relevant info into an object that implements the subcmd.Cmd interface. cmd := command{db: db} // Pass that object to subcmd.Run, // which will parse a subcommand and its flags // from the remaining command-line arguments // and run them. err = subcmd.Run(context.Background(), cmd, flag.Args()) if err != nil { panic(err) } } // Type command implements the subcmd.Cmd interface, // meaning that it can report its subcommands, // their names, // and their parameters and types // via the Subcmds method. type command struct { db *sql.DB } func (c command) Subcmds() subcmd.Map { return subcmd.Commands( // The "list" subcommand takes one flag, -reverse. "list", c.list, "list employees", subcmd.Params( "-reverse", subcmd.Bool, false, "reverse order of list", ), // The "add" subcommand takes no flags but one positional argument. "add", c.add, "add new employee", subcmd.Params( "name", subcmd.String, "", "employee name", ), ) } // The implementation of a subcommand takes a context object, // the values of its parsed flags and positional arguments, // and a slice of remaining command-line arguments // (which could be used in another call to subcmd.Run // to implement a sub-subcommand). // It can optionally return an error. func (c command) list(ctx context.Context, reverse bool, _ []string) error { query := "SELECT name FROM employees ORDER BY name" if reverse { query += " DESC" } rows, err := c.db.QueryContext(ctx, query) if err != nil { return err } defer rows.Close() for rows.Next() { // ...print the employee name contained in this row... } return rows.Err() } // Implementation of the "add" subcommand. func (c command) add(ctx context.Context, name string, _ []string) error { _, err := c.db.ExecContext(ctx, "INSERT INTO employees (name) VALUES ($1)", name) return err }
Output:
Index ¶
- Constants
- Variables
- func Check(subcmd Subcmd) error
- func CheckMap(m Map) error
- func FlagSet(ctx context.Context) *flag.FlagSet
- func ParseEnv(ptr interface{}) error
- func Run(ctx context.Context, c Cmd, args []string) error
- type Cmd
- type Copier
- type FuncTypeErr
- type HelpRequestedErr
- type Map
- type MissingSubcmdErr
- type Param
- type ParamDefaultErr
- type ParseErr
- type Prefixer
- type Subcmd
- type Type
- type UnknownSubcmdErr
- type UsageErr
Examples ¶
Constants ¶
const EnvVar = "SUBCMD_ENV"
EnvVar is the name of the environment variable used by Run to pass the JSON-encoded Cmd to a subprocess. Use ParseEnv to decode it. See Prefixer.
Variables ¶
var ErrTooFewArgs = errors.New("too few arguments")
ErrTooFewArgs is the error when not enough arguments are supplied for required positional parameters.
Functions ¶
func Check ¶ added in v2.1.0
Check checks that the type of subcmd.F matches the expectations set by subcmd.Params:
- It must be a function;
- It must return no more than one value;
- If it returns a value, that value must be of type error;
- It must take an initial context.Context parameter;
- It must take a final []string or ...string parameter;
- The length of subcmd.Params must match the number of parameters subcmd.F takes (not counting the initial context.Context and final []string parameters);
- Each parameter in subcmd.Params must match the corresponding parameter in subcmd.F.
It also checks that the default value of each parameter in subcmd.Params matches the parameter's type.
func FlagSet ¶
FlagSet produces the flag.FlagSet used in a call to a Subcmd function.
func ParseEnv ¶ added in v2.1.0
func ParseEnv(ptr interface{}) error
ParseEnv parses the value of the SUBCMD_ENV environment variable, placing the result in the value pointed to by ptr, which must be a pointer of a suitable type. Executables that implement subcommands should run this at startup.
func Run ¶
Run runs the subcommand of c named in args[0].
That subcommand specifies zero or more flags and zero or more positional parameters. The remaining values in args are parsed to populate those.
The subcommand's function is invoked with the given context object, the parsed flag and positional-parameter values, and a slice of the values remaining in args after parsing.
Flags are parsed using a new flag.FlagSet, which is placed into the context object passed to the subcommand's function. The FlagSet can be retrieved if needed with the FlagSet function. No flag.FlagSet is present if the subcommand has no flags.
Flags are always optional, and have names beginning with "-". Positional parameters may be required or optional. Optional positional parameters have a trailing "?" in their names.
Calling Run with an empty args slice produces a MissingSubcmdErr error.
Calling Run with an unknown subcommand name in args[0] produces an UnknownSubcmdErr error, unless the unknown subcommand is "help", in which case the result is a HelpRequestedErr, or unless c is also a Prefixer.
If c is a Prefixer and the subcommand name is both unknown and not "help", then an executable is sought in $PATH with c's prefix plus the subcommand name. (For example, if c.Prefix() returns "foo-" and the subcommand name is "bar", then the executable "foo-bar" is sought.) If one is found, it is executed with the remaining args as arguments, and a JSON-marshaled copy of c in the environment variable SUBCMD_ENV (that can be parsed by the subprocess using ParseEnv).
If there are not enough values in args to populate the subcommand's required positional parameters, the result is ErrTooFewArgs.
If argument parsing succeeds, Run returns the error produced by calling the subcommand's function, if any.
Types ¶
type Cmd ¶
type Cmd interface { // Subcmds returns this Cmd's subcommands as a map, // whose keys are subcommand names and values are Subcmd objects. // The Commands() function is useful in building this map. Subcmds() Map }
Cmd is a command that has subcommands. It tells Run how to parse its subcommands, and their flags and positional parameters, and how to run them.
type Copier ¶ added in v2.3.0
Copier is a flag.Value that can copy itself. Your type should implement Copier if you want to be able to use the same default value for multiple arguments without changes to one (e.g. when parsing command-line options into it) affecting the others.
type FuncTypeErr ¶ added in v2.1.0
type FuncTypeErr struct { // Got is the type of the F field. Got reflect.Type // Want is the expected function type implied by the Params field. // Note: there are four variations on this type: // variadic vs. non-variadic, and error-returning vs. non-error-returning. // FuncTypeErr means a function matched none of those types, // but for simplicity Want contains only one of them // (the non-variadic, error-returning one). Want reflect.Type }
FuncTypeErr means a Subcmd's F field has a type that does not match the function signature implied by its Params field.
func (FuncTypeErr) Error ¶ added in v2.1.0
func (e FuncTypeErr) Error() string
type HelpRequestedErr ¶
type HelpRequestedErr struct {
// contains filtered or unexported fields
}
HelpRequestedErr is a usage error returned when the "help" pseudo-subcommand-name is used.
func (*HelpRequestedErr) Detail ¶
func (e *HelpRequestedErr) Detail() string
Detail implements Usage.
func (*HelpRequestedErr) Error ¶
func (e *HelpRequestedErr) Error() string
type Map ¶
Map is the type of the data structure returned by Cmd.Subcmds and by Commands. It maps a subcommand name to its Subcmd structure.
func Commands ¶
func Commands(args ...interface{}) Map
Commands is a convenience function for producing the Map needed by an implementation of Cmd.Subcmd. It takes arguments in groups of two or four, one group per subcommand.
The first argument of a group is the subcommand's name, a string. The second argument of a group may be a Subcmd, making this a two-argument group.
If it's not a Subcmd, then this is a four-argument group, whose second through fourth arguments are:
- the function implementing the subcommand;
- a short description of the subcommand;
- the list of parameters for the function, a slice of Param (which can be produced with the Params function).
These are used to populate a Subcmd. See Subcmd for a description of the requirements on the implementing function.
A call like this:
Commands( "foo", foo, "is the foo subcommand", Params( "-verbose", Bool, false, "be verbose", ), "bar", bar, "is the bar subcommand", Params( "-level", Int, 0, "barness level", ), )
is equivalent to:
Map{ "foo": Subcmd{ F: foo, Desc: "is the foo subcommand", Params: []Param{ { Name: "-verbose", Type: Bool, Default: false, Doc: "be verbose", }, }, }, "bar": Subcmd{ F: bar, Desc: "is the bar subcommand", Params: []Param{ { Name: "-level", Type: Int, Default: 0, Doc: "barness level", }, }, }, }
Note, if a parameter's type is Value, then its default value must be a flag.Value.
This function panics if the number or types of the arguments are wrong.
type MissingSubcmdErr ¶
type MissingSubcmdErr struct {
// contains filtered or unexported fields
}
MissingSubcmdErr is a usage error returned when Run is called with an empty args list.
func (*MissingSubcmdErr) Detail ¶
func (e *MissingSubcmdErr) Detail() string
Detail implements Usage.
func (*MissingSubcmdErr) Error ¶
func (e *MissingSubcmdErr) Error() string
type Param ¶
type Param struct { // Name is the flag name for the parameter. // Flags must have a leading "-", as in "-verbose". // Positional parameters have no leading "-". // Optional positional parameters have a trailing "?", as in "optional?". Name string // Type is the type of the parameter. Type Type // Default is a default value for the parameter. // Its type must be suitable for Type. // If Type is Value, // then Default must be a [flag.Value]. // It may optionally also be a [Copier], qv. Default interface{} // Doc is a docstring for the parameter. Doc string }
Param is one parameter of a Subcmd.
func Params ¶
func Params(a ...interface{}) []Param
Params is a convenience function for producing the list of parameters needed by a Subcmd. It takes 4n arguments, where n is the number of parameters. Each group of four is:
- the name for the parameter, a string (e.g. "-verbose" for a -verbose flag);
- the type of the parameter, a Type constant;
- the default value of the parameter,
- the doc string for the parameter.
Note, if a parameter's type is Value, then its default value must be a flag.Value.
This function panics if the number or types of the arguments are wrong.
func ToFlagSet ¶
func ToFlagSet(params []Param) (fs *flag.FlagSet, ptrs []reflect.Value, positional []Param, err error)
ToFlagSet takes a slice of Param and produces:
- a flag.FlagSet,
- a list of properly typed pointers (or in the case of a Value-typed Param, a flag.Value) in which to store the results of calling Parse on the FlagSet,
- a list of positional Params that are not part of the resulting FlagSet.
On a successful return, len(ptrs)+len(positional) == len(params).
type ParamDefaultErr ¶ added in v2.1.0
type ParamDefaultErr struct {
Param Param
}
ParamDefaultErr is the error when a Param has a default value that is not of the correct type.
func (ParamDefaultErr) Error ¶ added in v2.1.0
func (e ParamDefaultErr) Error() string
type ParseErr ¶
type ParseErr struct {
Err error
}
ParseErr is the type of error returned when parsing a positional parameter according to its type fails.
type Prefixer ¶ added in v2.1.0
type Prefixer interface {
Prefix() string
}
Prefixer is an optional additional interface that a Cmd can implement. If it does, and a call to Run encounters an unknown subcommand, then before returning an error it will look for an executable in $PATH whose name is Prefix() plus the subcommand name. If it finds one, it is executed with the remaining args as arguments, and a JSON-marshaled copy of the Cmd in the environment variable SUBCMD_ENV (that can be parsed by the subprocess using ParseEnv).
type Subcmd ¶
type Subcmd struct { // F is the function implementing the subcommand. // Its signature must be one of the following: // // - func(context.Context, OPTS, []string) // - func(context.Context, OPTS, []string) error // - func(context.Context, OPTS, ...string) // - func(context.Context, OPTS, ...string) error // // where OPTS stands for a sequence of zero or more additional parameters // corresponding to the types in Params. // // A Param with type Value supplies a [flag.Value] to the function. // It's up to the function to type-assert the flag.Value to a more-specific type to read the value it contains. F interface{} // Params describes the parameters to F // (excluding the initial context.Context that F takes, and the final []string or ...string). Params []Param // Desc is a one-line description of this subcommand. Desc string }
Subcmd is one subcommand of a Cmd, and the value type in the Map returned by Cmd.Subcmds.
The function Check can be used to check that the type of the F field is a function with parameters matching those specified by the Params field, and also that each Param has a default value of the correct type.
type Type ¶
type Type int
Type is the type of a Param.
type UnknownSubcmdErr ¶
type UnknownSubcmdErr struct {
// contains filtered or unexported fields
}
UnknownSubcmdErr is a usage error returned when an unknown subcommand name is passed to Run as args[0].
func (*UnknownSubcmdErr) Detail ¶
func (e *UnknownSubcmdErr) Detail() string
Detail implements Usage.
func (*UnknownSubcmdErr) Error ¶
func (e *UnknownSubcmdErr) Error() string