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 will panic.
Dependency Injection ¶
To facilitate testing of Commands, both Run and RunSingle support dependency injection of the standard streams (stdin, stdout, and stderr). 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.
Example (Run) ¶
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 repititions"` } 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, }, ) }
Output:
Example (Run_single) ¶
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 repititions"` } 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", } // Execute the program. flags.RunSingle(&cli) }
Output:
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 repititions (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 repititions (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.
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 repititions"` } 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: "Hello", Name: "world", Count: 1, } 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 repititions"` } 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 repititions (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 repititions"` } 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 repititions"` } 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.
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 repititions"` } 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 repititions"` } 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) 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 repititions (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 repititions"` } 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 repititions (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 which 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).
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.
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