Documentation ¶
Overview ¶
Package flags implements command-line flags parsing, configured with struct tags.
Instead of imperatively configuring the command-line flags for an application, this package instead uses reflection to build the command-line parser from a struct. This package also supports the configuration of sub-commands.
All of the functions in this package must first build a model of the CLI based on type of the command, and the command's struct tags. If any errors are found in the description of the CLI, then this compilation step will panic.
Dependency Injection ¶
To facilitate testing of Commands, both Run and RunSingle support dependency injection of the standard streams (stdin, stdout, and stderr). To opt into the injection, the command must also implement the IOInjector interface.
For testing, users can create tests where they call Parse to configure a command, inject memory buffers for input and output, and then call the method Run directly.
When run normally, either by calling Run or RunSingle, this package will inject the three standard streams from the os package.
Bash Completion ¶
Although not listed on the usage, all commands built using this package also support a hidden command-line option that will generate bash completions. Assuming that the appropriate environment variables have been set (typically done by the shell), the following call will list possible completions.
./example --completion
To enable completions, one should execute one of the following commands:
eval "$(./example --completion=bash)" ./single --completion=fish | source eval "$(./example --completion=zsh)"
Man Pages ¶
The information used to generate help can also be used to write a man page. In the case of a program with sub-commands, this will be N+1 man pages.
See https://man.sr.ht/~rj/flags/man_pages.md
Common Flags ¶
Flags common across multiple sub-commands (sometimes called persistent flags), can be managed using [struct embedding](https://go.dev/ref/spec).
Index ¶
- func Parse(command interface{}, args []string) error
- func PrintSingleUsage(w io.Writer, command interface{}, options ...Option)
- func Run(commands map[string]Command, options ...Option)
- func RunSingle(command Command, options ...Option)
- type Command
- type Describer
- type IOInject
- type IOInjector
- type Option
- type Value
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Parse ¶
Parse parses command-line flags, and stores the result in the struct. The first command should be a pointer to a struct whose fields have tags to specify the argument names.
Parse expects to see only the command-line arguments. For example, one could use os.Args[1:].
Example ¶
package main import ( "fmt" "git.sr.ht/~rj/flags" ) func main() { // Initialize the CLI and provide defaults for the parameters. cli := struct { Greeting string `flags:"-g,--greet" description:"Salutation to use."` Name string `flags:"" description:"Name of person to greet."` }{ Greeting: "Hello", Name: "world", } // Parse the command line. err := flags.Parse(&cli, // Typically provide os.Args[1:] here []string{"-g", "Bonjour", "le monde"}) must(err) // To be replaced with proper error handling. // Take action. fmt.Printf("%s, %s!\n", cli.Greeting, cli.Name) } func must(err error) { if err != nil { fmt.Printf("error: %s\n", err) } }
Output: Bonjour, le monde!
Example (Bool) ¶
package main import ( "fmt" "strings" "git.sr.ht/~rj/flags" ) func main() { // Initialize the CLI and provide defaults for the parameters. cli := struct { A bool `flags:"-a"` B bool `flags:"-b"` C bool `flags:"-c"` D bool `flags:"-d"` Quiet bool `flags:"--quiet"` }{} // Parse the command line. err := flags.Parse(&cli, // Typically provide os.Args[1:] here strings.Fields("-abc --quiet")) must(err) // To be replaced with proper error handling. // Take action. fmt.Printf("%v %v %v %v %v\n", cli.A, cli.B, cli.C, cli.D, cli.Quiet) } func must(err error) { if err != nil { fmt.Printf("error: %s\n", err) } }
Output: true true true false true
Example (Repeated) ¶
package main import ( "fmt" "strings" "git.sr.ht/~rj/flags" ) func main() { // Initialize the CLI and provide defaults for the parameters. cli := struct { Count int `flags:""` Files []string `flags:""` }{} // Parse the command line. err := flags.Parse(&cli, // Typically provide os.Args[1:] here strings.Fields("123 file1.txt file2.txt file3.txt")) must(err) // To be replaced with proper error handling. // Take action. fmt.Printf("%d %v\n", cli.Count, cli.Files) } func must(err error) { if err != nil { fmt.Printf("error: %s\n", err) } }
Output: 123 [file1.txt file2.txt file3.txt]
Example (Slice) ¶
package main import ( "fmt" "strings" "git.sr.ht/~rj/flags" ) func main() { // Initialize the CLI and provide defaults for the parameters. cli := struct { Files []string `flags:"-f,--file"` }{} // Parse the command line. err := flags.Parse(&cli, // Typically provide os.Args[1:] here strings.Fields("-f file1.txt -f=file2.txt -ffile3.txt --file file4.txt --file=file5.txt")) must(err) // To be replaced with proper error handling. // Take action. fmt.Printf("%v\n", cli.Files) } func must(err error) { if err != nil { fmt.Printf("error: %s\n", err) } }
Output: [file1.txt file2.txt file3.txt file4.txt file5.txt]
func PrintSingleUsage ¶
PrintSingleUsage generates and prints the usage for a command to the writer. It prints the same message as would happen in a call to RunSingle, if the arguments requested help.
Example ¶
// Initialize the CLI and provide defaults for the parameters. cli := ExampleCLI{ Greeting: "Hello", Name: "world", Count: 1, } flags.PrintSingleUsage(os.Stdout, &cli, flags.Name("example"))
Output: Usage: example [options]... <name> example (--help | -h) Describe the command. Required Arguments: <name> Name of person to greet. Optional Arguments: -g=<string>, --greet=<string> Salutation to use (default "Hello") -c=<int>, --count=<int> Number of repetitions (default 1)
Example (Options) ¶
// Initialize the CLI and provide defaults for the parameters. cli := ExampleCLI{ Greeting: "Hello", Name: "world", Count: 1, } flags.PrintSingleUsage( os.Stdout, &cli, flags.Name("example"), flags.Version("v0.1.2"), flags.Description("Example help from a godoc example."), )
Output: Usage: example [options]... <name> example (--help | -h) example (--version | -v) Example help from a godoc example. Required Arguments: <name> Name of person to greet. Optional Arguments: -g=<string>, --greet=<string> Salutation to use (default "Hello") -c=<int>, --count=<int> Number of repetitions (default 1)
Example (Repeated) ¶
package main import ( "os" "git.sr.ht/~rj/flags" ) func main() { // Initialize the CLI and provide defaults for the parameters. cli := struct { Count int `flags:"" description:"Number of times to repeat the operation."` Files []string `flags:"" description:"Some files needed for the operation."` }{} // Parse the command line. flags.PrintSingleUsage( os.Stdout, &cli, flags.Name("example"), flags.Version("v0.1.2"), flags.Description("Example help from a godoc example."), ) }
Output: Usage: example <count> <files>... example (--help | -h) example (--version | -v) Example help from a godoc example. Required Arguments: <count> Number of times to repeat the operation. <files>... Some files needed for the operation.
func Run ¶
Run parses the command-line to select and configure a Command. It then runs that command.
Run will check for standard options to see if the user has requested help, or possibly to print version information. When a standard option is present, Run will handle the option and then exit the program.
If there are any errors parsing the command line, or when executing the command, Run will abort the program.
If the selected command supports the IOInjector interface, it will be called with the standard streams (os.Stdin, os.Stdout, and os.Stderr).
Example ¶
package main import ( "fmt" "git.sr.ht/~rj/flags" ) type ExampleCLI struct { Greeting string `flags:"-g,--greet" description:"Salutation to use"` Name string `flags:"" description:"Name of person to greet."` Count int `flags:"-c,--count" description:"Number of repetitions"` } func (cli *ExampleCLI) Run() error { fmt.Printf("%s, %s!\n", cli.Greeting, cli.Name) return nil } func (cli *ExampleCLI) Description() string { return "Describe the command." } func main() { // Initialize the commands for CLI. Initial values for the fields will be // used as defaults. command1 := ExampleCLI{ Greeting: "Hello", Name: "world", Count: 1, } command2 := ExampleCLI{ Greeting: "Hello", Name: "world", Count: 1, } // Execute the program. The call to run will handle all command-line // parsing, will report errors, and call the appropriate command. flags.Run( map[string]flags.Command{ "cmd1": &command1, "cmd2": &command2, }, flags.Name("example"), flags.Version("v0.1.2"), // For testing only, specify the command-line argument. flags.Args("./example", "cmd1", "-g", "Hallo", "welt"), ) }
Output: Hallo, welt!
Example (Command_help) ¶
package main import ( "fmt" "git.sr.ht/~rj/flags" ) type ExampleCLI struct { Greeting string `flags:"-g,--greet" description:"Salutation to use"` Name string `flags:"" description:"Name of person to greet."` Count int `flags:"-c,--count" description:"Number of repetitions"` } func (cli *ExampleCLI) Run() error { fmt.Printf("%s, %s!\n", cli.Greeting, cli.Name) return nil } func (cli *ExampleCLI) Description() string { return "Describe the command." } func main() { // Initialize the CLI and provide defaults for the parameters. command1 := ExampleCLI{ Greeting: "Hello", Name: "world", Count: 1, } command2 := ExampleCLI{ Greeting: "Bonjour", Name: "le monde", Count: 1, } // Print the automatically generated usage to standard out. flags.Run( map[string]flags.Command{ "cmd1": &command1, "cmd2": &command2, }, flags.Name("example"), flags.Version("v0.1.2"), // For testing only, specify the command-line argument. flags.Args("./example", "cmd1", "--help"), // For testing only, prevent call to os.Exit that normally occurs after // printing help. flags.Testing(true), ) }
Output: Usage: example cmd1 [options]... <name> example cmd1 (--help | -h) Describe the command. Required Arguments: <name> Name of person to greet. Optional Arguments: -g=<string>, --greet=<string> Salutation to use (default "Hello") -c=<int>, --count=<int> Number of repetitions (default 1)
Example (Help) ¶
package main import ( "fmt" "git.sr.ht/~rj/flags" ) type ExampleCLI struct { Greeting string `flags:"-g,--greet" description:"Salutation to use"` Name string `flags:"" description:"Name of person to greet."` Count int `flags:"-c,--count" description:"Number of repetitions"` } func (cli *ExampleCLI) Run() error { fmt.Printf("%s, %s!\n", cli.Greeting, cli.Name) return nil } func (cli *ExampleCLI) Description() string { return "Describe the command." } func main() { // Initialize the CLI and provide defaults for the parameters. command1 := ExampleCLI{ Greeting: "Hello", Name: "world", Count: 1, } command2 := ExampleCLI{ Greeting: "Bonjour", Name: "le monde", Count: 1, } // Print the automatically generated usage to standard out. flags.Run( map[string]flags.Command{ "cmd1": &command1, "cmd2": &command2, }, flags.Name("example"), flags.Version("v0.1.2"), // For testing only, specify the command-line argument. flags.Args("./example", "--help"), // For testing only, prevent call to os.Exit that normally occurs after // printing help. flags.Testing(true), ) }
Output: Usage: example <command> [options]... <arguments>... example (--help | -h) example (--version | -v) Available commands: cmd1 Describe the command. cmd2 Describe the command.
Example (Version) ¶
package main import ( "fmt" "git.sr.ht/~rj/flags" ) type ExampleCLI struct { Greeting string `flags:"-g,--greet" description:"Salutation to use"` Name string `flags:"" description:"Name of person to greet."` Count int `flags:"-c,--count" description:"Number of repetitions"` } func (cli *ExampleCLI) Run() error { fmt.Printf("%s, %s!\n", cli.Greeting, cli.Name) return nil } func (cli *ExampleCLI) Description() string { return "Describe the command." } func main() { // Initialize the CLI and provide defaults for the parameters. command1 := ExampleCLI{ Greeting: "Hello", Name: "world", } command2 := ExampleCLI{ Greeting: "Bonjour", Name: "le monde", } // Print the automatically generated usage to standard out. flags.Run( map[string]flags.Command{ "cmd1": &command1, "cmd2": &command2, }, flags.Name("example"), flags.Version("v0.1.2"), // For testing only, specify the command-line argument. flags.Args("./example", "--version"), // For testing only, prevent call to os.Exit that normally occurs after // printing version. flags.Testing(true), ) }
Output: example (v0.1.2)
func RunSingle ¶
RunSingle parses the command-line to configure a Command. It then runs that command.
RunSingle will check for standard options to see if the user has requested help, or possibly to print version information. When a standard option is present, RunSingle will handle the option and then exit the program.
If there are any errors parsing the command line, or when executing the command, Run will abort the program.
If the command supports the IOInjector interface, it will be called with the standard streams (os.Stdin, os.Stdout, and os.Stderr).
Example ¶
package main import ( "fmt" "git.sr.ht/~rj/flags" ) type ExampleCLI struct { Greeting string `flags:"-g,--greet" description:"Salutation to use"` Name string `flags:"" description:"Name of person to greet."` Count int `flags:"-c,--count" description:"Number of repetitions"` } func (cli *ExampleCLI) Run() error { fmt.Printf("%s, %s!\n", cli.Greeting, cli.Name) return nil } func (cli *ExampleCLI) Description() string { return "Describe the command." } func main() { // Initialize the CLI and provide defaults for the parameters. cli := ExampleCLI{ Greeting: "Hello", Name: "world", } flags.RunSingle(&cli, flags.Name("example"), flags.Version("v0.1.2"), // For testing only, specify the command-line argument. flags.Args("./example", "-g", "Hallo", "welt"), ) }
Output: Hallo, welt!
Example (Help) ¶
package main import ( "fmt" "git.sr.ht/~rj/flags" ) type ExampleCLI struct { Greeting string `flags:"-g,--greet" description:"Salutation to use"` Name string `flags:"" description:"Name of person to greet."` Count int `flags:"-c,--count" description:"Number of repetitions"` } func (cli *ExampleCLI) Run() error { fmt.Printf("%s, %s!\n", cli.Greeting, cli.Name) return nil } func (cli *ExampleCLI) Description() string { return "Describe the command." } func main() { // Initialize the CLI and provide defaults for the parameters. cli := ExampleCLI{ Greeting: "Hello", Name: "world", Count: 1, } flags.RunSingle(&cli, flags.Name("example"), flags.Version("v0.1.2"), // For testing only, specify the command-line argument. flags.Args("./example", "--help"), // For testing only, prevent call to os.Exit that normally occurs after // printing help. flags.Testing(true), ) }
Output: Usage: example [options]... <name> example (--help | -h) example (--version | -v) Describe the command. Required Arguments: <name> Name of person to greet. Optional Arguments: -g=<string>, --greet=<string> Salutation to use (default "Hello") -c=<int>, --count=<int> Number of repetitions (default 1)
Example (Reflow) ¶
package main import ( "fmt" "git.sr.ht/~rj/flags" ) type ExampleCLI struct { Greeting string `flags:"-g,--greet" description:"Salutation to use"` Name string `flags:"" description:"Name of person to greet."` Count int `flags:"-c,--count" description:"Number of repetitions"` } func (cli *ExampleCLI) Run() error { fmt.Printf("%s, %s!\n", cli.Greeting, cli.Name) return nil } func (cli *ExampleCLI) Description() string { return "Describe the command." } func main() { const lorem = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent molestie facilisis mauris, quis tristique justo viverra id. Integer efficitur consectetur rhoncus. Ut et eros et augue sagittis rutrum in ac diam. Nunc eget erat vel nisi ullamcorper euismod. Phasellus pretium pharetra maximus. Mauris eget lectus ac massa luctus semper. Donec faucibus nisi et elit tempor congue at non ligula. Fusce.` //nolint:lll // Initialize the CLI and provide defaults for the parameters. cli := ExampleCLI{ Greeting: "Hello", Name: "world", Count: 1, } flags.RunSingle(&cli, flags.Name("example"), flags.Description(lorem), flags.Version("v0.1.2"), // For testing only, specify the command-line argument. flags.Args("./example", "--help"), // For testing only, prevent call to os.Exit that normally occurs after // printing help. flags.Testing(true), ) }
Output: Usage: example [options]... <name> example (--help | -h) example (--version | -v) Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent molestie facilisis mauris, quis tristique justo viverra id. Integer efficitur consectetur rhoncus. Ut et eros et augue sagittis rutrum in ac diam. Nunc eget erat vel nisi ullamcorper euismod. Phasellus pretium pharetra maximus. Mauris eget lectus ac massa luctus semper. Donec faucibus nisi et elit tempor congue at non ligula. Fusce. Required Arguments: <name> Name of person to greet. Optional Arguments: -g=<string>, --greet=<string> Salutation to use (default "Hello") -c=<int>, --count=<int> Number of repetitions (default 1)
Types ¶
type Describer ¶
Describer allows a command to set a description for itself, useful when print usage.
type IOInject ¶
IOInject is meant to be embedded in Commands that wish to have dependency injection for standard input, standard output, and standard error.
type IOInjector ¶
IOInjector is an interface for dependency injection of input and output.
Example ¶
package main import ( "bytes" "fmt" "git.sr.ht/~rj/flags" ) type InjectorCommand struct { flags.IOInject ExampleCLI } func (cmd *InjectorCommand) Run() error { fmt.Fprintf(cmd.Stdout, "%s, %s!\n", cmd.Greeting, cmd.Name) return nil } func main() { cmd := InjectorCommand{ IOInject: flags.IOInject{}, ExampleCLI: ExampleCLI{ Greeting: "Hello", Name: "world", }, } stdout := bytes.NewBuffer(nil) cmd.Inject(nil, stdout, nil) _ = cmd.Run() fmt.Printf(">>> %s", stdout.String()) }
Output: >>> Hello, world!
type Option ¶
type Option func(*config)
Option specifies additionial metadata to help with printing usage information, or with parsing command-line arguments.
func Args ¶
Args overrides the command-line arguments used for parsing. The arguments should start with the program name (similar to os.Args).
func Description ¶
Description sets a description of the program, to be printed after basic usage information. The description can span multiple paragraphs (separated by a single newline).
func Executable ¶ added in v0.7.0
Executable overrides the return for calls to os.Executable.
type Value ¶
Value is the interface that allow custom types to be set by command-line flags.
Set is called once for each flag present.
Types can also support the encoding.TextUnmarshaler interface to support conversion from text.
Example ¶
package main import ( "fmt" "time" "git.sr.ht/~rj/flags" ) type TimeOption struct { time.Time } func (t *TimeOption) String() string { return t.Time.String() } func (t *TimeOption) Set(value string) (err error) { t.Time, err = time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", value) return err } func main() { cli := struct { Now TimeOption `flags:"--time"` }{ TimeOption{Time: time.Now()}, } err := flags.Parse(&cli, []string{"--time", "2006-01-02 15:04:05 -0700 MST"}) must(err) // To be replaced with proper error handling. fmt.Println(cli.Now.String()) }
Output: 2006-01-02 15:04:05 -0700 MST
Source Files ¶
Directories ¶
Path | Synopsis |
---|---|
cmds
|
|
makeman
Command makeman ingests the JSON output from a program built with git.sr.ht/~rj/flags, and generates man pages.
|
Command makeman ingests the JSON output from a program built with git.sr.ht/~rj/flags, and generates man pages. |
The folders under this one contains stand-alone examples of both types of commands possible with the flags package.
|
The folders under this one contains stand-alone examples of both types of commands possible with the flags package. |
command
Command is an example CLI where that uses sub-commands.
|
Command is an example CLI where that uses sub-commands. |
single
Single is an example CLI where there are no sub-commands.
|
Single is an example CLI where there are no sub-commands. |
internal
|
|
completion
Package completion supports bash completion for commands built using package flags.
|
Package completion supports bash completion for commands built using package flags. |
parse
Package parse is used to convert strings when the target type is unknown at compile-time.
|
Package parse is used to convert strings when the target type is unknown at compile-time. |
teststdio
Package teststdio provides functions to change the global variables os.Stdout and os.Stderr so that they can be read by the test program.
|
Package teststdio provides functions to change the global variables os.Stdout and os.Stderr so that they can be read by the test program. |