| package cli |
| |
| import ( |
| "io" |
| "os" |
| "sync" |
| ) |
| |
| // CLI contains the state necessary to run subcommands and parse the |
| // command line arguments. |
| type CLI struct { |
| // Args is the list of command-line arguments received excluding |
| // the name of the app. For example, if the command "./cli foo bar" |
| // was invoked, then Args should be []string{"foo", "bar"}. |
| Args []string |
| |
| // Commands is a mapping of subcommand names to a factory function |
| // for creating that Command implementation. |
| Commands map[string]CommandFactory |
| |
| // Name defines the name of the CLI. |
| Name string |
| |
| // Version of the CLI. |
| Version string |
| |
| // HelpFunc and HelpWriter are used to output help information, if |
| // requested. |
| // |
| // HelpFunc is the function called to generate the generic help |
| // text that is shown if help must be shown for the CLI that doesn't |
| // pertain to a specific command. |
| // |
| // HelpWriter is the Writer where the help text is outputted to. If |
| // not specified, it will default to Stderr. |
| HelpFunc HelpFunc |
| HelpWriter io.Writer |
| |
| once sync.Once |
| isHelp bool |
| subcommand string |
| subcommandArgs []string |
| |
| isVersion bool |
| } |
| |
| // NewClI returns a new CLI instance with sensible defaults. |
| func NewCLI(app, version string) *CLI { |
| return &CLI{ |
| Name: app, |
| Version: version, |
| HelpFunc: BasicHelpFunc(app), |
| } |
| |
| } |
| |
| // IsHelp returns whether or not the help flag is present within the |
| // arguments. |
| func (c *CLI) IsHelp() bool { |
| c.once.Do(c.init) |
| return c.isHelp |
| } |
| |
| // IsVersion returns whether or not the version flag is present within the |
| // arguments. |
| func (c *CLI) IsVersion() bool { |
| c.once.Do(c.init) |
| return c.isVersion |
| } |
| |
| // Run runs the actual CLI based on the arguments given. |
| func (c *CLI) Run() (int, error) { |
| c.once.Do(c.init) |
| |
| // Just show the version and exit if instructed. |
| if c.IsVersion() && c.Version != "" { |
| c.HelpWriter.Write([]byte(c.Version + "\n")) |
| return 1, nil |
| } |
| |
| // Attempt to get the factory function for creating the command |
| // implementation. If the command is invalid or blank, it is an error. |
| commandFunc, ok := c.Commands[c.Subcommand()] |
| if !ok || c.Subcommand() == "" { |
| c.HelpWriter.Write([]byte(c.HelpFunc(c.Commands) + "\n")) |
| return 1, nil |
| } |
| |
| command, err := commandFunc() |
| if err != nil { |
| return 0, err |
| } |
| |
| // If we've been instructed to just print the help, then print it |
| if c.IsHelp() { |
| c.HelpWriter.Write([]byte(command.Help() + "\n")) |
| return 1, nil |
| } |
| |
| return command.Run(c.SubcommandArgs()), nil |
| } |
| |
| // Subcommand returns the subcommand that the CLI would execute. For |
| // example, a CLI from "--version version --help" would return a Subcommand |
| // of "version" |
| func (c *CLI) Subcommand() string { |
| c.once.Do(c.init) |
| return c.subcommand |
| } |
| |
| // SubcommandArgs returns the arguments that will be passed to the |
| // subcommand. |
| func (c *CLI) SubcommandArgs() []string { |
| c.once.Do(c.init) |
| return c.subcommandArgs |
| } |
| |
| func (c *CLI) init() { |
| if c.HelpFunc == nil { |
| c.HelpFunc = BasicHelpFunc("app") |
| |
| if c.Name != "" { |
| c.HelpFunc = BasicHelpFunc(c.Name) |
| } |
| } |
| |
| if c.HelpWriter == nil { |
| c.HelpWriter = os.Stderr |
| } |
| |
| c.processArgs() |
| } |
| |
| func (c *CLI) processArgs() { |
| for i, arg := range c.Args { |
| |
| if c.subcommand == "" { |
| // Check for version and help flags if not in a subcommand |
| if arg == "-v" || arg == "-version" || arg == "--version" { |
| c.isVersion = true |
| continue |
| } |
| if arg == "-h" || arg == "-help" || arg == "--help" { |
| c.isHelp = true |
| continue |
| } |
| } |
| |
| // If we didn't find a subcommand yet and this is the first non-flag |
| // argument, then this is our subcommand. j |
| if c.subcommand == "" && arg[0] != '-' { |
| c.subcommand = arg |
| |
| // The remaining args the subcommand arguments |
| c.subcommandArgs = c.Args[i+1:] |
| } |
| } |
| } |