README

Build status Coverage GoReportCard API documentation

Simple and complete API for building command line applications in Go

Module cli provides a simple, fast and complete API for building command line applications in Go. In contrast to other libraries the emphasis is put on the definition and validation of positional arguments, handling of options from all levels in a single block as well as a minimalistic set of dependencies.

The core of the module is the command, option and argument parsing logic. After a successful parsing the command action is evaluated passing a slice of (validated) positional arguments and a map of (validated) options. No more no less.

Definition

co := cli.NewCommand("checkout", "checkout a branch or revision").
  WithShortcut("co").
  WithArg(cli.NewArg("revision", "branch or revision to checkout")).
  WithOption(cli.NewOption("branch", "Create branch if missing").WithChar('b').WithType(cli.TypeBool)).
  WithOption(cli.NewOption("upstream", "Set upstream for the branch").WithChar('u').WithType(cli.TypeBool)).
  WithAction(func(args []string, options map[string]string) int {
    // do something
    return 0
  })

add := cli.NewCommand("add", "add a remote").
  WithArg(cli.NewArg("remote", "remote to add")).

rmt := cli.NewCommand("remote", "Work with git remotes").
  WithCommand(add)

app := cli.New("git tool").
  WithOption(cli.NewOption("verbose", "Verbose execution").WithChar('v').WithType(cli.TypeBool)).
  WithCommand(co).
  WithCommand(rmt)
  // no action attached, just print usage when executed

os.Exit(app.Run(os.Args, os.Stdout))

Execution

Given the above definition is for a git client, e.g. gitc, running gitc with no arguments or with -h will produce (the exit code will be 1 in the former case, because the action is missing, and 0 in the latter, because help explicitly requested):

gitc [--verbose]

Description:
    git tool

Options:
    -v, --verbose   Verbose execution

Sub-commands:
    git checkout    checkout a branch or revision
    git remote      Work with git remotes

Running gitc with arguments matching e.g. the checkout definition, gitc co -vbu dev or gitc checkout -v --branch -u dev will execute the command as expected. Running into a parsing error, e.g. by providing an unknown option gitc co -f dev, will output a parsing error and a short usage string:

fatal: unknown flag -f
usage: gitc checkout [--verbose] [--branch] [--upstream] <revision>
Copyright (c) 2017. Oleg Sklyar and teris.io. MIT license applies. All rights reserved.
Expand ▾ Collapse ▴

Documentation

Overview

    Package cli provides a simple, fast and complete API for building command line applications in Go. In contrast to other libraries additional emphasis is put on the definition and validation of positional arguments and consistent usage outputs combining options from all command levels into one block.

    Index

    Constants

    This section is empty.

    Variables

    This section is empty.

    Functions

    func Parse

    func Parse(a App, appargs []string) (invocation []string, args []string, opts map[string]string, err error)

      Parse parses the original application arguments into the command invocation path (application -> first level command -> second level command etc.), a list of validated positional arguments matching the command being invoked (the last one in the invocation path) and a map of validated options matching one of the invocation path elements, from the top application down to the command being invoked. An error is returned if a command is not found or arguments or options are invalid. In case of an error, the invocation path is normally also computed and returned (the content of arguments and options is not guaranteed). See `App.parse`

      func Usage

      func Usage(a App, invocation []string, w io.Writer) error

        Usage prints out the complete usage string.

        Types

        type Action

        type Action func(args []string, options map[string]string) int

          Action defines a function type to be executed for an application or a command. It takes a slice of validated positional arguments and a map of validated options (with all value types encoded as strings) and returns a Unix exit code (success: 0).

          type App

          type App interface {
          	// Description returns the application description to be output in the usage.
          	Description() string
          	// Args returns required and optional positional arguments for the top-level application.
          	Args() []Arg
          	// Options permitted for the top-level application and all sub-commands.
          	Options() []Option
          	// Commands returns the set of first-level sub-commands for the application.
          	Commands() []Command
          	// Action returns the application action when no sub-command is specified.
          	Action() Action
          
          	// WithArg adds a positional argument to the application. Specifying last application/command
          	// argument as optional permits unlimited number of further positional arguments (at least one
          	// optional argument needs to be specified in the definition for this case).
          	WithArg(arg Arg) App
          	// WithOption adds a permitted option to the application and all sub-commands.
          	WithOption(opt Option) App
          	// WithCommand adds a first-level sub-command to the application.
          	WithCommand(cmd Command) App
          	// WithAction sets the action function to execute after successful parsing of commands, arguments
          	// and options to the top-level application.
          	WithAction(action Action) App
          
          	// Parse parses the original application arguments into the command invocation path (application ->
          	// first level command -> second level command etc.), a list of validated positional arguments matching
          	// the command being invoked (the last one in the invocation path) and a map of validated options
          	// matching one of the invocation path elements, from the top application down to the command being invoked.
          	// An error is returned if a command is not found or arguments or options are invalid. In case of an error,
          	// the invocation path is normally also computed and returned (the content of arguments and options is not
          	// guaranteed).
          	Parse(appargs []string) (invocation []string, args []string, opts map[string]string, err error)
          	// Run parses the argument list and runs the command specified with the corresponding options and arguments.
          	Run(appargs []string, w io.Writer) int
          	// Usage prints out the full usage help.
          	Usage(invocation []string, w io.Writer) error
          }

            App defines a CLI application parameterizable with sub-commands, arguments and options.

            func New

            func New(descr string) App

              New creates a new CLI App.

              type Arg

              type Arg interface {
              	// Key defines how the argument will be shown in the usage string.
              	Key() string
              	// Description returns the description of the argument usage
              	Description() string
              	// Type defines argument type. Default is string, which is not validated,
              	// other types are validated by simple string parsing into boolean, int and float.
              	Type() ValueType
              	// Optional specifies that an argument may be omitted. No non-optional arguments
              	// should follow an optional one (no validation for this scenario as this is
              	// the definition time exception, rather than incorrect input at runtime).
              	Optional() bool
              
              	// WithType sets the argument type.
              	WithType(at ValueType) Arg
              	// AsOptional sets the argument as optional.
              	AsOptional() Arg
              }

                Arg defines a positional argument. Arguments are validated for their count and their type. If the last defined argument is optional, then an unlimited number of arguments can be passed into the call, otherwise an exact count of positional arguments is expected. Obviously, optional arguments can be omitted. No validation is done for the invalid case of specifying an optional positional argument before a required one.

                func NewArg

                func NewArg(key, descr string) Arg

                  NewArg creates a new positional argument.

                  type Command

                  type Command interface {
                  	// Key returns the command name.
                  	Key() string
                  	// Shortcut returns the command shortcut if not empty.
                  	Shortcut() string
                  	// Description returns the command description to be output in the usage.
                  	Description() string
                  	// Args returns required and optional positional arguments for this command.
                  	Args() []Arg
                  	// Options permitted for this command and its sub-commands.
                  	Options() []Option
                  	// Commands returns the set of sub-commands for this command.
                  	Commands() []Command
                  	// Action returns the command action when no further sub-command is specified.
                  	Action() Action
                  
                  	// WithShortcut adds a (shorter) command alias, e.g. `co` for `checkout`.
                  	WithShortcut(shortcut string) Command
                  	// WithArg adds a positional argument to the command. Specifying last application/command
                  	// argument as optional permits unlimited number of further positional arguments (at least one
                  	// optional argument needs to be specified in the definition for this case).
                  	WithArg(arg Arg) Command
                  	// WithOption adds a permitted option to the command and all sub-commands.
                  	WithOption(opt Option) Command
                  	// WithCommand adds a next-level sub-command to the command.
                  	WithCommand(cmd Command) Command
                  	// WithAction sets the action function for this command.
                  	WithAction(action Action) Command
                  }

                    Command defines a named sub-command in a command-tree of an application. A complete path to the terminal command e.g. `git remote add` must be defined ahead of any options or positional arguments. These are parsed first.

                    func NewCommand

                    func NewCommand(key, descr string) Command

                      NewCommand creates a new command to be added to an application or to another command.

                      type Option

                      type Option interface {
                      	// Key returns the complete key of an option (used with the -- notation), required.
                      	Key() string
                      	// CharKey returns a single-character key of an option (used with the - notation), optional.
                      	CharKey() rune
                      	// Description returns the option description for the usage string.
                      	Description() string
                      	// Type returns the option type (string by default) to be used to decide if a value is required and for
                      	// value validation.
                      	Type() ValueType
                      
                      	// WithChar sets the char key for the option.
                      	WithChar(char rune) Option
                      	// WithType sets the option value type.
                      	WithType(ft ValueType) Option
                      }

                        Option defines an application or command option.

                        Boolean options do not need a value, as their presence implicitly means `true`. All other option types need a value. When options are specified using their char keys their need to be prepended by a single dash (-) and can be joined together (-zxv). Hhowever, only boolean options requiring no further value may occupy a non-terminal position of a join. A value must follow a char option as the next argument, same applies for a non-boolean option at the terminal position of an option join, e.g. `-fc 1` means `-f -c 1`, where 1 is the argument for `-c`.

                        Non-char, complete, options must be prepended with a double dash (--) and their value must be provided in the same argument after the equal sign, e.g. --count=1. Similarly here, boolean options require no value. Empty values are supported for complete (non-char) string options only by providing no value after the equal sign, e.g. `--default=`. Equal signs can be used within the option value, e.g. `--default=a=b=c` specifies the `a=b=c` string as a value for `--default`.

                        Every option must have a `complete` name as these are used as keys to pass options to the action. In case only a char option is desired, a complete key with the same single char should be defined.

                        Options can be used at any position after the command, arbitrarily intermixed with positional arguments. In contrast to positional arguments the order of options is not preserved.

                        func NewOption

                        func NewOption(key, descr string) Option

                          NewOption creates a new option with a given key and description.

                          type ValueType

                          type ValueType int

                            ValueType defines the type of permitted argument and option values.

                            const (
                            	TypeString ValueType = iota
                            	TypeBool
                            	TypeInt
                            	TypeNumber
                            )

                              ValueType constants for string, boolean, int and number options and arguments.