Documentation ¶
Overview ¶
Package shell provides a simple shell interface and an API to register commands and sub-commands for use within it.
Basics ¶
Create a new app and add a command:
// The first argument is the app name and the second controls whether // or not the default commands help and exit are added. app := shell.NewApp("MyApp", true) if err := app.AddCommand(Command{ Name: "test", Synopsis: "run some tests", Usage: "${name} ${shortFlags}: Do some things and run some tests and more detailed information. ${flags}", SetFlags: func(ctx *shell.Context) { ctx.Set("top", ctx.FlagSet().Int("top", 12, "example top-level flag")) }, Main: func(ctx *shell.Context) shell.ExitStatus { ctx.App().Println("Hello world!", *ctx.MustGet("top").(*int)) return ExitCmd }, SubCommands: []Command{ { Name: "secondary", SetFlags: func(ctx *Context) { ctx.Set("second", ctx.FlagSet().Int("second", 21, "example second-level flag")) }, Main: func(ctx *Context) ExitStatus { ctx.App().Println("Hello world from a sub-command!") return ExitCmd }, }, } })
Start the main loop:
app.Main()
And you're all set!
Index ¶
- Variables
- type App
- func (app *App) AddCommand(cmd Command) error
- func (app *App) ExecuteString(input string) (ExitStatus, error)
- func (app *App) GetByName(name string) (*Command, error)
- func (app *App) Main() ExitStatus
- func (app *App) Print(a ...interface{})
- func (app *App) Printf(format string, a ...interface{})
- func (app *App) Println(a ...interface{})
- type Command
- type Context
- func (context *Context) App() *App
- func (context *Context) Command() *Command
- func (context *Context) Delete(name string)
- func (context *Context) FlagSet() *flag.FlagSet
- func (context *Context) Get(name string) (interface{}, error)
- func (context *Context) MustGet(name string) interface{}
- func (context *Context) Parent() *Command
- func (context *Context) Set(name string, value interface{})
- func (context *Context) ShouldGet(name string) interface{}
- type ErrNoCmd
- type ErrParseFlags
- type ErrParseInput
- type ExitStatus
Constants ¶
This section is empty.
Variables ¶
var DefaultCommands = []*Command{ { Name: "exit", Synopsis: "exit shell", Usage: "${name} ${shortFlags}:\n\n${flags}", SetFlags: func(ctx *Context) { ctx.Set("flagOnlyShell", ctx.FlagSet().Bool("shell-only", false, "exit only the shell, "+ "returning to the main program")) }, Main: func(ctx *Context) ExitStatus { if ctx.FlagSet().NArg() > 0 { return ExitUsage } if *ctx.ShouldGet("flagOnlyShell").(*bool) { return ExitShell } return ExitAll }, }, { Name: "help", Synopsis: "list existing commands and their synopsis", Usage: "${name} [<command name>]", Main: func(ctx *Context) ExitStatus { switch ctx.FlagSet().NArg() { case 0: list := make([]string, 0) for _, command := range ctx.App().Commands { if command.Name == "help" { continue } list = append(list, fmt.Sprintf("\t%s\t\t%s\n", command.Name, command.Synopsis)) } sort.Strings(list) ctx.App().Printf("Available commands:\n%s\n\nFor more information, type `help <command name>`.", strings.Join(list, "")) case 1: requested, err := ctx.App().GetByName(ctx.FlagSet().Arg(0)) if err != nil { ctx.App().Printf("%s: command not found\n", ctx.FlagSet().Arg(0)) return ExitCmd } if requested.Usage == "" { ctx.App().Printf("%s\t\t%s\n", requested.Name, requested.Synopsis) } else { ctx.App().Printf("%s\n", requested.Usage) } default: return ExitUsage } return ExitCmd }, }, }
DefaultCommands defines the following top-level commands: help and exit.
var DefaultSubCommands = []Command{ { Name: "commands", Synopsis: "list all sub-command names", Usage: `${name}: Print a list of all sub-commands.`, Main: func(ctx *Context) ExitStatus { if ctx.FlagSet().NArg() > 0 { return ExitUsage } for _, subCmd := range ctx.Parent().SubCommands { ctx.App().Println(subCmd.Name) } return ExitCmd }, }, { Name: "flags", Synopsis: "describe all known top-level flags", Usage: `${name} [<sub-command>]: With an argument, print all flags of <sub-command>. Else, print a description of all known top-level flags. (The basic help information only discusses the most generally important top-level flags.)`, Main: func(ctx *Context) ExitStatus { flags := ctx.FlagSet() if flags.NArg() > 1 { return ExitUsage } var reqCmd *Command if flags.NArg() == 0 { reqCmd = ctx.Parent() } else { for _, cmd := range ctx.Parent().SubCommands { if flags.Arg(0) == cmd.Name { reqCmd = &cmd break } } if reqCmd == nil { ctx.App().Printf("%s %s: sub-command not found", ctx.Parent().Name, flags.Arg(0)) return ExitCmd } } reqCtx := reqCmd.NewContext() if reqCmd.SetFlags != nil { reqCmd.SetFlags(reqCtx) } reqCtx.FlagSet().PrintDefaults() return ExitCmd }, }, { Name: "help", Synopsis: "describe sub-commands and their syntax", Usage: `${name} [<sub-command>]: With an argument, prints detailed information on the use of the specified sub-command. With no argument, prints a list of all commands and a brief description of each.`, Main: func(ctx *Context) ExitStatus { parent := ctx.Parent() switch ctx.FlagSet().NArg() { case 0: list := make([]string, 0) for _, subCmd := range parent.SubCommands { if subCmd.Name == "help" { continue } list = append(list, fmt.Sprintf("\t%s\t\t%s\n", subCmd.Name, subCmd.Synopsis)) } sort.Strings(list) ctx.App().Printf("Usage: %s <sub-command> <sub-command args>\n\n"+ "Sub-commands:\n%s", parent.Name, strings.Join(list, "")) case 1: var reqCmd *Command for _, cmd := range parent.SubCommands { if ctx.FlagSet().Arg(0) == cmd.Name { reqCmd = &cmd break } } if reqCmd == nil { ctx.App().Printf("%s %s: sub-command not found", parent.Name, ctx.FlagSet().Arg(0)) return ExitCmd } if reqCmd.Usage == "" { ctx.App().Printf("%s\t\t%s\n", reqCmd.Name, reqCmd.Synopsis) } else { ctx.App().Printf("%s\n", reqCmd.Usage) } default: return ExitUsage } return ExitCmd }, }, }
DefaultSubCommands defines the following sub-commands: commands, flags, and help.
Functions ¶
This section is empty.
Types ¶
type App ¶
type App struct { // Name is the name of the program. No restrictions are applied. Defaults // to filepath.Base(os.Args[0]). Name string // Commands holds all commands attached to the App. Commands []*Command // Output controls the destination for general messages emitted by the App. Output io.Writer // ErrOutput controls the destination for usage and error messages. ErrOutput io.Writer // Input controls the reader used to fetch user input. Input io.ReadCloser }
App is the main structure that makes up a single shell. Through it commands are created and managed. App is not intended to be directly created or manipulated, instead its methods and NewApp should be utilized.
func NewApp ¶
NewApp creates an App and configures its logger. The first argument defines the name of the App and defaults to filepath.Base(os.Args[0]) if blank. A set of default commands are also automatically added unless false is passed as an argument.
func (*App) AddCommand ¶
AddCommand takes a Command and adds it to the App. If the command or any of its sub-commands are invalid an error is returned.
func (*App) ExecuteString ¶
func (app *App) ExecuteString(input string) (ExitStatus, error)
ExecuteString takes what is usually some user input and attempts to execute a command based on the input. If no matching command exists an ErrNoCmd is returned. If the input string is invalid an ErrParseInput is returned. If a command is successfully executed, it's ExitStatus is returned, otherwise ExecuteString defaults to ExitCmd. An ErrParseFlags may be returned in event of a failure when parsing the input flags.
func (*App) GetByName ¶
GetByName takes a string and returns a pointer to a command or an error if no command by that name exists.
func (*App) Main ¶
func (app *App) Main() ExitStatus
Main is the App's main loop. It accepts user input infinitely until some command returns an ExitStatus of ExitShell. Any errors that occur are not propagated back up but rather printed to the App's Output.
func (*App) Print ¶
func (app *App) Print(a ...interface{})
Print prints to the App's Output. Arguments are handled in the manner of fmt.Print.
type Command ¶
type Command struct { // Name is required and should be as concise as possible. It may not // contain any spaces. Name string // Synopsis should contain a short description of the command. It usually // should not be more than a single sentence. Synopsis string // Usage should contain a detailed description of the command. There are no // limitations to its length. Several sequences are substituted with // information relating to the command when found within the usage string: // `${name}` is substituted with the name of the command, ${fullName} with // the full name of the command (including parent command name if the // command is a sub-command), ${flags} with the help information for the // command flags as described by/ flag.PrintDefaults, and ${shortFlags} for // a short list of all registered flags in the format of [-<flag name>] and // separated with spaces. Usage string // SetFlags should register any flags with the flag.FlagSet available // through the Context and store their result within via Context.[Get|Set]. // SetFlags should not attempt to parse flags since it does not have access // to the complete input string. SetFlags func(*Context) // Main is required and contains the command logic itself. If SetFlags // exists, flags will be parsed immediately before Main is called and // the results should be accessible via the Context. Main func(*Context) ExitStatus // SubCommands should contain an arbitrary number of Commands. If the name // of a valid sub-command directly follows the name of this command in some // user input, the sub-command will be preferred over this Command. // Otherwise, this Command will be executed. SubCommands []Command // PreventDefaultSubCommands controls whether the sub-commands defined // within the exported table PreventDefaultSubCommands should be added by // default. If no sub-commands are defined in the SubCommands array, this // option is disregarded. If left blank, default sub-commands are added. PreventDefaultSubCommands bool // contains filtered or unexported fields }
Command is a top-level command within a shell App. It may contain an arbitrary number of sub-command.
func (*Command) Execute ¶
func (cmd *Command) Execute(input []string) (ExitStatus, error)
Execute takes an array of strings, usually representing some user input retrieved from the shell loop. It then executes this Command, first parsing the input for flags. If an error occurs while parsing flags, it is returned.
func (*Command) FullName ¶
FullName returns the full name of the command, checking if it has a parent and if so prepending it to its own name.
func (*Command) GetSubCommand ¶
GetSubCommand attempts to fetch a sub-command by name, returning a pointer to the sub-command if successful and an error if it does not exist.
func (*Command) Match ¶
Match takes an array of strings, usually representing some user input retrieved from the shell loop. If the input does not call for this command an error is returned, otherwise Match checks if the input calls for a sub- command, returning either it or this Command if no match is found.
func (*Command) NewContext ¶
NewContext returns an empty context prepared for this command.
type Context ¶
type Context struct {
// contains filtered or unexported fields
}
Context is a type that is passed to each handler when a Command is executed and allows arbitrary information to be shared between handlers besides providing access to specifics about the command itself. Context may be created manually due to its simplicity, but it is often helpful to utilize NewContext.
func NewContext ¶
NewContext creates a new context given an App, a Command, a flag.FlagSet, and optionally a parent Command.
func (*Context) App ¶
App returns the connected shell App. Warning: if none exists a nil pointer will be returned.
func (*Context) Command ¶
Command returns the Command for which the Context is acting. Warning: if none exists a nil pointer will be returned.
func (*Context) Delete ¶
Delete takes a string and delete the value stored at that index. No error is returned regardless of whether a deletion actually occurs.
func (*Context) FlagSet ¶
FlagSet returns the flag.FlagSet for use with command handler functions. Warning: if none exists a nil pointer will be returned.
func (*Context) Get ¶
Get takes a string and returns its value or an error if the key does not exist.
func (*Context) Parent ¶
Parent returns the parent Command. Warning: if none exists a nil pointer will be returned.
type ErrNoCmd ¶
type ErrNoCmd struct {
Name string
}
ErrNoCmd is returned from ExecuteString if the input does not call a valid command.
type ErrParseFlags ¶
ErrParseFlags is returned from Command.Execute is the FlagSet fails to parse.
func (*ErrParseFlags) Error ¶
func (err *ErrParseFlags) Error() string
Error implements the error interface for ErrParseFlags.
type ErrParseInput ¶
type ErrParseInput struct {
Input string
}
ErrParseInput is returned from ExecuteString is the input for some reason cannot be parsed. Currently occurs only when input is empty.
func (*ErrParseInput) Error ¶
func (err *ErrParseInput) Error() string
Error implements the error interface for ErrParseInput
type ExitStatus ¶
type ExitStatus int
ExitStatus is returned from Command handlers to instruct the program how to react to its completion.
const ( // ExitCmd exits only the command. ExitCmd ExitStatus = iota // ExitUsage does the same as ExitCmd but also prints the Usage string for // the command. ExitUsage // ExitShell exits the shell's infinite loop, but does not itself trigger // the entire program to exit. ExitShell // ExitAll exits not only the shell loop, but also the entire program. It // is, however, left up to the enclosing program to respect this. ExitAll )