Documentation
ΒΆ
Overview ΒΆ
Package cmdbox is a multicall, modular commander with embedded
documentation and tab completion handling that prioritizes modern, simple human-computer interactions from the command line.
Package cmdbox is a multicall, modular commander with embedded tab
completion and locale-driven documentation, that prioritizes modern, speakable human-computer interactions from the command line.
Index ΒΆ
- Variables
- func Call(caller *Command, name string, args ...string) error
- func CompleteCommand(x *Command) []string
- func Delete(names ...string)
- func Dups() []string
- func Execute(a ...string)
- func ExecutedAs() string
- func Exit()
- func ExitError(err ...interface{})
- func ExitOff()
- func ExitOn()
- func ExitSyntaxError(a string)
- func ExitUnimplemented(a string)
- func Init()
- func JSON() string
- func Names() []string
- func Print()
- func Rename(from, to string)
- func Set(name string, x *Command)
- func YAML() string
- type Command
- func (x *Command) Add(sigs ...string)
- func (x *Command) AddHelp()
- func (x *Command) Call(name string, args ...string) error
- func (x *Command) CommandRequired() bool
- func (x *Command) Complete()
- func (x *Command) Help() string
- func (x *Command) JSON() string
- func (x *Command) Legal() string
- func (x *Command) MissingArg(a string) error
- func (x *Command) Print()
- func (x *Command) PrintHelp()
- func (x *Command) Resolve(name string) *Command
- func (x *Command) ResolveDelegate(args []string) *Command
- func (x *Command) Sigs() *util.StringMap
- func (x Command) String() string
- func (x *Command) Title() string
- func (x *Command) Titles(indent, max int) string
- func (x *Command) UnexpectedArg(a string) error
- func (x *Command) Unimplemented(a string) error
- func (x *Command) UpdateUsage()
- func (x *Command) UsageError() error
- func (x *Command) YAML() string
- type CommandMap
- func (m *CommandMap) Delete(keys ...string)
- func (m CommandMap) Dups() []string
- func (m *CommandMap) Get(key string) *Command
- func (m *CommandMap) Init()
- func (m CommandMap) JSON() string
- func (m CommandMap) Names() []string
- func (m CommandMap) Print()
- func (m CommandMap) Rename(from, to string)
- func (m *CommandMap) Set(key string, val *Command)
- func (m CommandMap) Slice(names ...string) []*Command
- func (m CommandMap) String() string
- func (m CommandMap) YAML() string
- type CompFunc
- type Method
Examples ΒΆ
- Add (Simple)
- Add (With_Duplicates)
- Add (With_Subcommands)
- Call (Caller_Subcommand)
- Call (Nil_Caller)
- Command.Add
- Command.Complete (Commands)
- Command.Complete (CompFunc)
- Command.JSON
- Command.Legal
- Command.Resolve
- Command.Resolve (Bork)
- Command.Resolve (Subcommand)
- Command.Sigs
- Command.Title
- Command.Titles
- Command.Unimplemented
- Command.UsageError
- Command.YAML
- Delete
- Execute (No_Method)
- Get
- Init
- JSON
- Names
- NewCommand (Commands)
- NewCommand (Dup)
- NewCommand (Invalid)
- NewCommand (Simple)
- NewCommand (Subcommand)
- Rename
- Resolve
- Set
- Slice
- YAML
Constants ΒΆ
This section is empty.
Variables ΒΆ
var BadType = func(v interface{}) error { return fmt.Errorf(m_bad_type, v) }
BadType returns an error containing the bad type attempted.
var CallerRequired = func() error { return fmt.Errorf(m_missing_caller) }
CallerRequired retuns an error indicating a Command was used incorrectly (as designed by the developer) and that is requires being called from something else.
var Color = true
Color sets the default output mode for interactive terminals. Set to false to force uncolored output for testing, etc. Non-interactive terminals have color disabled by default (unless ForceColor is set).
var DEBUG bool
DEBUG is set when os.Getenv("CMDBOX_DEBUG") is set to anything. Produces verbose debugging logs to stderr to help cmdbox users develop robust tools.
var DefaultComplete = CompFunc(CompleteCommand)
DefaultComplete is assigned CompleteCommand by default but can be assigned any valid CompFunc to override it. This function is called to perform completion for any Command that does not implement its own Command.CompFunc.
var DoNotExit bool
DoNotExit is a utility for disabling any call to os.Exit via any caller in order to trap panics and use them for testing, etc.
var ForceColor = false
ForceColor forces color output no matter what the default Color value is. This can be used for testing or associating with configuration parameters (for example, when a user has a pager that supports color output).
var Harmless = func(msg ...string) error { if len(msg) > 0 { return fmt.Errorf("%v", msg[0]) } return fmt.Errorf("") }
Harmless returns an error that is mostly designed to trigger an error exit status. This is useful for help and commands like it to help the user disambiguate significant output from just help and other error output.
var MissingArg = func(name string) error { return fmt.Errorf(m_missing_arg, name) }
MissingArg returns an error stating that the name of the parameter for which no argument was found.
var Reg = NewCommandMap()
Reg is the internal register (map) of Commands. See CommandMap and Add. Use caution when manipulating Reg directly.
var SyntaxError = func(msg string) error { return fmt.Errorf(m_syntax_error, msg) }
SyntaxError returns an error with the message stating the problem.
var TrapPanic = func() { if r := recover(); r != nil { ExitError(r) } }
TrapPanic recovers from any panic and more gracefully displays the error as an exit message. It is used to gaurantee that no cmdbox composite command will ever panic (exiting instead). It can be redefined to behave differently or set to an empty func() to allow the panic to blow up with its full trace log.
var TrapPanicNoExit = func() { if r := recover(); r != nil { fmt.Println(r) } }
TrapPanicNoExit is same as TrapPanic without exiting. It prints the panic instead (useful for testing).
var UnexpectedArg = func(name string) error { return fmt.Errorf(m_unexpected_arg, name) }
UnexpectedArg returns an error stating that the argument passed was unexpected in the given context.
var Unimplemented = func(a string) error { return fmt.Errorf(m_unimplemented, a) }
Unimplemented returns an unimplemented error for the Command passed. This function may be overriden by command modules from their init() and main() procedures to change behavior for everything in the composite command. See "unimplemented" in Messages.
var Unresolvable = func(msg string) error { return fmt.Errorf(m_unresolvable, msg) }
Unresolvable returns an error stating the command method could not be found in the internal registry.
var UsageError = func(x *Command) error { return fmt.Errorf("usage: %v %v", x.Name, x.Usage) }
UsageError returns an error containing the usage string suitable for printing directly. This function may be overriden by CmdBox command modules from their init and main methods to change behavior for everything in the composite command.
Functions ΒΆ
func Call ΒΆ
Call allows any Command in the internal register to be called directly by name. The first argument is an optional pointer to the calling Command, the second is the required name, and the third is an optional list of string arguments (or nil). Resolve is first called to get the Command from the internal registry and lookup the proper Method and any argument shifting required. If no Method is returned Call returns Unimplemented. Otherwise, Method is called with its arguments and error result returned. See command.Call, Resolve, Command, Execute, and ExampleCall as well.
Example (Caller_Subcommand) ΒΆ
package main import ( "fmt" "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing caller := cmdbox.Add("foo", "h|help") x := cmdbox.Add("foo help") x.Method = func(args ...string) error { fmt.Printf("help for foo %v\n", args) return nil } cmdbox.Call(caller, "help") cmdbox.Call(nil, "foo help") cmdbox.Call(caller, "help", "with", "args") }
Output: help for foo [] help for foo [] help for foo [with args]
Example (Nil_Caller) ΒΆ
package main import ( "fmt" "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing x := cmdbox.Add("greet") x.Method = func(args ...string) error { fmt.Println("hello") return nil } cmdbox.Call(nil, "greet") }
Output: hello
func CompleteCommand ΒΆ added in v0.0.8
CompleteCommand takes a pointer to a Command (x) returning a list of lexigraphically sorted combination of strings from x.Commands that are found in the internal register and x.Params that match the current completion context with any x.Hidden strings removed. Returns an empty list if anything fails. Note that no assertion validating that the specified command names exist in the register. See the Command.Complete method and comp subpackage.
func Delete ΒΆ added in v0.1.0
func Delete(names ...string)
Delete deletes one or more commands from the internal register.
Example ΒΆ
package main import ( "fmt" "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing cmdbox.Add("foo") cmdbox.Add("bar") fmt.Println(cmdbox.Names()) cmdbox.Delete("bar") fmt.Println(cmdbox.Names()) }
Output: [bar foo] [foo]
func Dups ΒΆ added in v0.1.0
func Dups() []string
Dups returns key strings of duplicates (which can then be easily renamed). Keys are sorted in lexicographic order. See Rename.
func Execute ΒΆ
func Execute(a ...string)
Execute is the main entrypoint into a CmdBox composite command and is always called from a main() function. In fact, most won't need the optional argument at all since it is inferred by the name of the executable. See ExecutedAs.
package main import "github.com/rwxrob/cmdbox" func main() { cmdbox.Execute() }
Execute also traps all panics and eventually Calls the Command matching the inferred name from the Reg Commands register. If completion context is detected (see comp.Yes), Execute calls x.Complete instead of Calling it. Execute is guaranteed to always exit the program cleanly. See Call, TrapPanic, and Command.
Example (No_Method) ΒΆ
package main import ( "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing cmdbox.ExitOff() defer cmdbox.ExitOn() cmdbox.Add("foo", "some") cmdbox.Execute("foo") }
Output: usage: foo some
func ExecutedAs ΒΆ added in v0.0.10
func ExecutedAs() string
ExecutedAs returns the multicall inferred name of the executable as it was called during the init() phase. The multicall approach (akin to BusyBox) allows the binary to be renamed, hard or soft linked, or copied, effectively changing the behavior simply by changing the resulting changed name. For security reasons this name may never be changed at runtime. When the Execute function is called without any arguments the ExecutedAs value is inferred automatically. Irrelevant suffixes are removed (currently, only .exe).
func ExitError ΒΆ
func ExitError(err ...interface{})
ExitError prints err and exits with 1 return value unless DoNotExit has been set to true.
func ExitSyntaxError ΒΆ added in v0.7.8
func ExitSyntaxError(a string)
ExitSyntaxError prints the bad syntax and calls ExitError().
func ExitUnimplemented ΒΆ
func ExitUnimplemented(a string)
ExitUnimplemented calls Unimplemented and calls ExitError().
func Init ΒΆ added in v0.0.16
func Init()
Init initializes (or re-initialized) the package status and empties the internal commands register (without changing its reference). Init is primarily intended for testing to reset the cmdbox package.
Example ΒΆ
package main import ( "fmt" "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing fmt.Println(cmdbox.Names()) cmdbox.Add("foo") fmt.Println(cmdbox.Names()) cmdbox.Init() fmt.Println(cmdbox.Names()) }
Output: [] [foo] []
func JSON ΒΆ
func JSON() string
JSON serializes the current internal package register of commands as JSON which can then be used to present documentation of the composite command in different forms. Also see YAML. Empty values are always omitted.
Example ΒΆ
package main import ( "fmt" "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing fmt.Println(cmdbox.JSON()) cmdbox.Add("foo") fmt.Println(cmdbox.JSON()) }
Output:
func Names ΒΆ added in v0.1.5
func Names() []string
Names returns a sorted list of all Command names in the internal register.
Example ΒΆ
package main import ( "fmt" "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing cmdbox.Add("foo") cmdbox.Add("bar") fmt.Println(cmdbox.Names()) }
Output: [bar foo]
func Print ΒΆ added in v0.0.16
func Print()
Print is shortcut for fmt.Println(cmdbox.YAML()) which is mostly only useful during testing.
func Rename ΒΆ added in v0.0.4
func Rename(from, to string)
Rename renames a Command in the Reg register by adding the new name with the same *Command and deleting the old one. This is useful when a name conflict causes New to append and underscore (_) to the duplicate's name. Rename can be called from init() at any point after the duplicate has been added to resolve the conflict. Note the order of init() execution --- while predictable --- is not always apparent. When in doubt do Rename from main(). Rename is safe for concurrency.
Example ΒΆ
package main import ( "fmt" "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing cmdbox.Add("foo") cmdbox.Add("foo") cmdbox.Rename("foo_", "bar") fmt.Println(cmdbox.Names()) }
Output: [bar foo]
func Set ΒΆ added in v0.1.0
Set sets the internal register for the given key to the given *Command pointer in a way that is safe for concurrency. Replaces entries that already exist. Note that although this allows register key names to refer to commands that have an actual x.Name that differs from the key this is discouraged, which is why Add and Rename should generally be used instead. Also see Add and Get.
Example ΒΆ
package main import ( "fmt" "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing foo := cmdbox.NewCommand("foo") cmdbox.Set("foo", foo) bar := cmdbox.NewCommand("bar") cmdbox.Set("bar", bar) cmdbox.Set("bar", foo) newbar := cmdbox.Get("bar") fmt.Println(newbar.Name) }
Output: foo
func YAML ΒΆ added in v0.1.0
func YAML() string
YAML serializes the current internal package register of commands as YAML which can then be used to present documentation of the composite command in different forms. Empty values are always omitted.
Example ΒΆ
package main import ( "fmt" "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing cmdbox.Add("foo") fmt.Print(cmdbox.YAML()) }
Output:
Types ΒΆ
type Command ΒΆ
type Command struct { Name string `json:"name,omitempty" yaml:",omitempty"` Summary string `json:"summary,omitempty" yaml:",omitempty"` Usage string `json:"usage,omitempty" yaml:",omitempty"` Version string `json:"version,omitempty" yaml:",omitempty"` Copyright string `json:"copyright,omitempty" yaml:",omitempty"` License string `json:"license,omitempty" yaml:",omitempty"` Description string `json:"description,omitempty" yaml:",omitempty"` Site string `json:"site,omitempty" yaml:",omitempty"` Source string `json:"source,omitempty" yaml:",omitempty"` Issues string `json:"issues,omitempty" yaml:",omitempty"` Commands *util.StringMap `json:"commands,omitempty" yaml:",omitempty"` Params []string `json:"params,omitempty" yaml:",omitempty"` Hidden []string `json:"hidden,omitempty" yaml:",omitempty"` Default string `json:"default,omitempty" yaml:",omitempty"` // Title() // Legal() CompFunc CompFunc `json:"-" yaml:"-"` Caller *Command `json:"-" yaml:"-"` Method Method `json:"-" yaml:"-"` sync.Mutex `json:"-" yaml:"-"` }
Command contains a Method or delegates to one or more other Commands by name. Typically a Command is created within an init() function by calling cmdbox.New:
import "github.com/rwxrob/cmdbox" func init() { // use x by convention x := cmdbox.New("greet","hi","hello") x.Method = func(args []string) error { if len(args) == 0 { args = append(args, "hi") } switch args[0] { case "hello": fmt.Println("*Hello!*") case "hi": fmt.Println("*Hello!*") default: return x.UsageError() } return nil } }
Providing the method, documentation, and tab completion rules in a single file providing a tight, clean view of the Command that is easy for humans and computers to quickly parse. Such Commands can also be placed into their own "side-effect" packages for importing into other code using the underscore identifier making for potentially very succinct commands.
import ( "github.com/rwxrob/cmdbox" _ "github.com/rwxrob/cmdbox-greet" ) func main() { cmdbox.Run() }
Or, it can be combined with others and composed into an entirely new monolith one of its commands:
import ( "github.com/rwxrob/cmdbox" _ "github.com/rwxrob/cmdbox-greet" _ "github.com/rwxrob/cmdbox-timer" _ "github.com/rwxrob/cmdbox-pomo" ) func init() { x := cmdbox.New("skeeziks","greet","timer","pomo") x.Usage = `[greet|timer|pomo]` // default: greet x.Summary = `a simple command line assistant` // notice no Method // ... }
This modularity allows a CmdBox monolith to compose in an unlimited number of commands. Hence the name CmdBox, a nod to BusyBox which does something similar but without the ability for sub-command composition and coding in Go.
A Command also includes all the documentation for the method as well as rules about how to handle tab completion so there is no need for extra shell code to be "eval"ed. In Bash, this happens by using the -C option of complete telling Bash to use the same command that it is completing for completion itself.
complete -C skeeziks skeeziks
And then the following will complete with the list passed to New():
skeeziks g<TAB> skeeziks greet
Additional builtin Commands are automatically composed into the monolith as well (unless disabled):
skeeziks h<TAB> skeeziks help
Tab completion rules default to the list of Commands and Params, but can be overriden per Command by defining and assigning an anonymous closure function to the CompFunc field (see CompFunc type).
If a Command.CompFunc is not assigned then Command.Complete will delegate to the package cmdbox.CompFunc passing it a pointer to the Command. In this way the default completion behavior of all Commands can be easily tested and changed, even at run time.
This allows for dynamic tab completion possibilities that have nothing to do with sub-Commands and can access program and system state for their determination.
Conventions for Text Field Values ΒΆ
When providing the text of the different fields of the the Command struct please keep the following conventional considerations in mind:
* Imagine your text being spoken by a conversational assistant * Avoid the use of acronymns and excessive punctuation * Resist the temptation to assign const and vars outside init
Fields such as Usage are obvious exceptions to these conventions.
Description ΒΆ
The Description contains a long Command description and can span multiple paragraphs, even pages. Use of backtick quotes (`) is preferred and text may be indended and wrapped anywhere. Paragraphs are recognized by a blank line between them. Keep Go documentation style guidelines in mind when writing them (headers on single line, four space indent for verbatim text, etc.)
Params ΒΆ
The Params list is for completion as well, specifically for things that are neither Commands nor actions to be handled by the Method but would be nice to have included in completion. Unlike Commands and Aliases, Params do not need to be valid names (see valid package). This allows them to be used for things such as default numeric values that may begin with a number or dash and other things that contain punctuation. These should not, however, be used to bypass the core requirement for speakable Commands and Aliases, and whenever possible, arguments as well. (For more complex completion, assign a custom x.CompFunc function.)
Hidden ΒΆ
The Hidden list is to keep commands from being completed or showing up in any general help documentation. They have to be specifically used or passed to help directly.
Examples ΒΆ
For examples of different Command structs search on GitHub for any project beginning with cmdbox- such as the following:
* https://github.com/rwxrob/cmdbox-greet * httpe://github.com/rwxrob/cmdbox-pomo
var Main *Command
Main is always set to the main command that was used for Execute. This can be useful from certain subcommands to query or call directly.
func Add ΒΆ added in v0.1.0
Add creates a new Command, adds it to the Reg internal register, and returns a pointer to it (assigned to 'x' by convention). The Add function is guaranteed to never return nil but will panic if invalid arguments are passed (see Validation below). Further initialization can be done with direct assignments to fields of x from within the init() function. Depending on the situation, you may find having a single init() that reuses x to Add multiple commands is desirable. Other times, you may wish to keep one Command per file usually named after the command.
First Argument is Command Name ΒΆ
The first argument is *always* the main Name by which it will be called and becomes x.Name. This uniquely identifies the Command and becomes the key used by cmdbox.Call to lookup the Command from the internal register for completion and execution. By convention, these command names should be complete words with no punctuation except for dot and applications will panic with names that do not follow this convention. It is sometimes desirable to group multiple commands using traditional dotted notation.
Command Names May Contain Two Words ΒΆ
The Name may contain two complete names separated by a single space. Only main commands should have a single name. Subcommands should always have two names to avoid naming collisions in Reg from other imported cmdbox modules and facilitate default tab completion. The first name is also the name of the parent command. This also removes indirection when called from Execute.
x := cmdbox.Add("foo help") x.Summary = `output help information for foo`
Commands May Have Subcommands ΒΆ
Any variadic arguments that follow will be directly passed to x.Add(). This provides a succinct summary of how the command may be called. As a convenience a h|help command can be added with x.AddHelp(), but is not included automatically. Make sure to AddHelp after Version, Copyright, and License have been set so that the LEGAL section will populate correctly.
x := cmdbox.Add("foo", "bar") x.Copyright = "2021 (c) Rob Muhlestein" x.AddHelp()
Subcommands May Have Aliases ΒΆ
Each argument passed in the list may be in signature form (rather than just a name) meaning it may have one or more aliases prefixed and bar-delimited which are added to the x.Commands Map:
x := cmdbox.Add("foo", "b|bar")
Aliases will appear in the help documentation (from AddHelp) but will not be options for completion even though they are valid commands. This is to prevent the completion list from getting overwhelming.
Default Usage Automatically Inferred ΒΆ
Since it is so common to declare everything up front for a new Command the x.Default will be set to an optional joined list of all commands and aliases as if the following where explicitly assigned:
x.Usage = `[b|bar]`
Of course, this can always be overridden explicitly by Command authors using the same assignment.
Command Method Has Priority ΒΆ
When the Command (x) is called from cmdbox.Call the x.Commands Map is used to delegate the call to a matching Command in the internal register if and only if the Command itself does not have a Method defined. Please read that last sentence carefully. See Call and x.Call for more about this delegation and how key names are matched to the internal register.
All but top-level Commands will usually assign a x.Method to handle the work of the Command. By convention the arguments should be named "args" if something is expected and "none" if not. The returned error type usually remains unnamed.
x.Method = func(args ...string) error { fmt.Println("would do something") return nil }
No Command Method Will Trigger Default Delegation ΒΆ
If the Command does not have an x.Method of its own, then the list of arguments passed to Add is assumed to be the signatures for other registered Commands.
No Assertion of Command Registration ΒΆ
Add does not validate that a potential command has been registered because the state of the internal register cannot be predicted at the specific time any init function is called. Not all Commands may yet have been registered before any other Add is called. This means runtime testing is required to check for errant calls to unregistered Commands (which otherwise produce a relatively harmless "unimplemented" error.)
Duplicate Names Append Underscore ΒΆ
Because CmdBox composite commands may be composed of packages imported from a rich eco-system of command module packages it is possible that two CmdBox modules might use conflicting names and need some resolution by the composite developer who is importing them.
Rather than override any Command previously added with an identical Name, Add simply adds an underscore (_) to the end of the name allowing it to be identified with Dups. Developer will know of such conflicts in advance and be able to easily correct them by calling the Rename function before Execute.
Example (Simple) ΒΆ
package main import ( "fmt" "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing cmdbox.Add("foo") fmt.Println(cmdbox.Names()) }
Output: [foo]
Example (With_Duplicates) ΒΆ
package main import ( "fmt" "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing cmdbox.Add("foo") cmdbox.Add("foo") fmt.Println(cmdbox.Dups()) fmt.Println(cmdbox.Names()) }
Output: [foo_] [foo foo_]
Example (With_Subcommands) ΒΆ
package main import ( "fmt" "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing cmdbox.Add("foo") cmdbox.Add("foo bar") fmt.Println(cmdbox.Names()) }
Output: [foo foo bar]
func Get ΒΆ added in v0.1.0
Get returns the *Command for key name if found.
Example ΒΆ
package main import ( "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing cmdbox.Add("foo", "bar") cmdbox.Get("foo").Print() }
Output: name: foo usage: bar commands: bar: bar
func NewCommand ΒΆ added in v0.1.0
NewCommand returns pointer to new initialized Command. See the New package function instead for creating a new Command that is also added to the Register. Minimal validation is done on the name and all arguments (subcommands, actions) to ensure a consistent user experience for all CmdBox commands (see valid subpackage for more). The Usage string is set to the default with UpdateUsage. Since calling NewCommand involves critical syntax checks a panic is thrown if invalid.
Example (Commands) ΒΆ
package main import ( "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing x := cmdbox.NewCommand("foo", "h|help", "l|ls|list") x.Print() }
Output: name: foo usage: '[h|help|l|list|ls]' commands: h: help help: help l: list list: list ls: list default: help
Example (Dup) ΒΆ
package main import ( "github.com/rwxrob/cmdbox" ) func main() { x := cmdbox.NewCommand("foo_") x.Print() }
Output: name: foo_ commands: {} default: help
Example (Invalid) ΒΆ
package main import ( "github.com/rwxrob/cmdbox" ) func main() { defer cmdbox.TrapPanicNoExit() cmdbox.NewCommand("Foo") // see valid/name.go for more }
Output: syntax error: invalid name (must be lowercase word): Foo
Example (Simple) ΒΆ
package main import ( "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing x := cmdbox.NewCommand("foo") x.Print() }
Output: name: foo commands: {}
Example (Subcommand) ΒΆ
package main import ( "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing x := cmdbox.NewCommand("foo bar") x.Print() }
Output: name: foo bar commands: {}
func Slice ΒΆ added in v0.1.0
Slice returns a slice of *Command pointers and fetched from the internal register that match the key names passed. If an entry is not found it is simply skipped. Will return an empty slice if none found.
Example ΒΆ
package main import ( "fmt" "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing cmdbox.Add("foo") cmdbox.Add("foo help") cmdbox.Add("bar") for _, x := range cmdbox.Slice("foo", "bar") { fmt.Println(x.Name) } }
Output: foo bar
func (*Command) Add ΒΆ
Add adds the list of Command signatures passed. A command signature consists of one or more more aliases separated by a bar (|) with the final word being the name of the actual Command. Aliases are a useful way to provide shortcuts when tab completion is not available and should generally be considered for every Command. Single letter aliases are common and encouraged.
Note that Add does not validate inclusion in the internal Register (Reg) since in many cases there may not yet be a Register entry, and in the case of actions handled entirely by the Command itself there never will be. See Command.Commands and Command.Run.
Example ΒΆ
package main import ( "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing x := cmdbox.NewCommand("foo") x.Add("l|ls|list", "h|help", "version") x.Print() }
Output: name: foo commands: h: help help: help l: list list: list ls: list version: version default: help
func (*Command) AddHelp ΒΆ added in v0.7.8
func (x *Command) AddHelp()
AddHelp adds a basic h|help subcommand sets x.Default to it if unset. As of v0.7.7 help is no longer automatically added to allows the lightest binaries possible. See PrintHelp and Help as well.
func (*Command) CommandRequired ΒΆ added in v0.7.8
CommandRequired returns true if x.Default is not set and there also is no x.Method found. Such commands always require a explicit subcommand or will produce a usage error.
func (*Command) Complete ΒΆ
func (x *Command) Complete()
Complete prints the possible strings based on the current Command and completion context. If the Commands CompFunc has been assigned (not nil) it is called and passed its own pointer. If CompFunc has not been assigned (is nil) then cmdbox.DefaultComplete is called instead. This allows Command authors to control their own completion or simply use the default. It also allows changing the default by assigning to the package cmdbox.DefaultComplete before calling cmdbox.Execute.
Example (Commands) ΒΆ
package main import ( "github.com/rwxrob/cmdbox" "github.com/rwxrob/cmdbox/comp" ) func main() { cmdbox.Init() // just for testing x := cmdbox.NewCommand("foo") x.Add("l|ls|list") comp.This = "l" x.Complete() }
Output: list
Example (CompFunc) ΒΆ
package main import ( "github.com/rwxrob/cmdbox" "github.com/rwxrob/cmdbox/comp" ) func main() { cmdbox.Init() // just for testing x := cmdbox.NewCommand("foo") x.CompFunc = func(x *cmdbox.Command) []string { // deliberatly always return the same thing // could add filter logic here return []string{"bar", "this"} } x.Complete() comp.This = "b" x.Complete() }
Output: bar this bar this
func (*Command) Help ΒΆ added in v0.2.0
Help returns a formatted string suitable for printing either to a file or to an interactive terminal. For a more structured form of the same information see YAML, JSON, Print, and PrintHelp.
func (*Command) JSON ΒΆ added in v0.1.0
JSON is shortcut for json.Marshal(x). See util.ToJSON.
Example ΒΆ
package main import ( "fmt" "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing x := cmdbox.NewCommand("foo", "help") fmt.Println(x.JSON()) fmt.Println(x.String()) fmt.Println(x) }
Output: {"name":"foo","usage":"[help]","commands":{"help":"help"},"default":"help"} {"name":"foo","usage":"[help]","commands":{"help":"help"},"default":"help"} {"name":"foo","usage":"[help]","commands":{"help":"help"},"default":"help"}
func (*Command) Legal ΒΆ added in v0.2.0
Legal returns a single line with the combined values of the Name, Version, Copyright, and License. If Version is empty or nil an empty string is returned instead. Legal() is used by the version builtin command to aggregate all the version information into a single output.
Example ΒΆ
package main import ( "fmt" "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing x := cmdbox.NewCommand("foo") fmt.Println(x.Legal()) x.Version = "v0.0.1" fmt.Println(x.Legal()) x.Copyright = "Copyright 2021 Rob" fmt.Println(x.Legal()) x.License = "Apache-2" fmt.Println(x.Legal()) }
Output: foo Copyright 2021 Rob foo v0.0.1 Copyright 2021 Rob License Apache-2
func (*Command) MissingArg ΒΆ added in v0.6.3
MissingArg returns cmdbox.MissingArg
func (*Command) Print ΒΆ added in v0.1.0
func (x *Command) Print()
Print outputs as YAML (nice when testing).
func (*Command) PrintHelp ΒΆ added in v0.2.0
func (x *Command) PrintHelp()
PrintHelp simply prints what Help returns.
func (*Command) Resolve ΒΆ added in v0.5.0
Resolve looks up the Command from the register based on the name passed. First it looks for a fully qualified entry in the register (x.Name + " " + name), then it just looks for the name alone. Returns nil if nothing found in the register. This method is particularly important because at init time when Commands are Added to the register their subcommands may not yet have been registered. Resolve allows this lookup to happen reliably later in runtime.
Example ΒΆ
package main import ( "fmt" "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing x := cmdbox.Add("foo", "bar") b := cmdbox.Add("bar") fmt.Println(b.Name) r := x.Resolve("bar") if r != nil { fmt.Println(r.Name) } }
Output: bar bar
Example (Bork) ΒΆ
package main import ( "fmt" "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing x := cmdbox.Add("foo me", "bork") r := x.Resolve("bork") fmt.Println(r) }
Output: <nil>
Example (Subcommand) ΒΆ
package main import ( "fmt" "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing x := cmdbox.Add("foo me", "bar") b := cmdbox.Add("bar") fmt.Println(b.Name) r := x.Resolve("bar") if r != nil { fmt.Println(r.Name) } }
Output: bar bar
func (*Command) ResolveDelegate ΒΆ added in v0.3.0
ResolveDelegate returns a Command pointer looked up from the internal register based on the following priority from more
- x.Caller.Name + " " + arg[0] as name
- cmdbox.Main.Name + " " + arg[0] as name
- arg[0] as name
This is a specialized lookup for Commands that are designed to operate on other Commands in the registry. See Help and Legal for examples. See Resolve for simpler resolution of Commands.
func (*Command) Sigs ΒΆ added in v0.5.0
Sigs returns a StringMap keyed to the Command.Names with signatures as values suitable for printing usage information. Hidden commands are omitted.
Example ΒΆ
package main import ( "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing x := cmdbox.Add("foo", "h|help", "version", "bar") x.Sigs().Print() }
Output: bar: bar help: h|help version: version
func (*Command) Title ΒΆ
Title returns a dynamic field of Name and Summary combined (if exists).
Example ΒΆ
package main import ( "fmt" "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing x := cmdbox.NewCommand("foo") fmt.Println(x.Title()) x.Summary = "just a foo" fmt.Println(x.Title()) }
Output: foo foo - just a foo
func (*Command) Titles ΒΆ added in v0.5.0
Titles returns a single string with the titles of each subcommand indented and with a maximum title signature length for justification. Hidden commands are not included.
Example ΒΆ
package main import ( "fmt" "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing x := cmdbox.Add("foo", "bar", "other", "p|print", "c|comp|complete") b := cmdbox.Add("bar") b.Summary = `does bar stuff` h := cmdbox.Add("other") h.Summary = `other stuff` p := cmdbox.Add("print") p.Summary = `prints stuff` c := cmdbox.Add("complete") c.Summary = `complete stuff` fmt.Println(x.Titles(0, 0)) fmt.Println(x.Titles(2, 0)) fmt.Println(x.Titles(4, 7)) }
Output: bar - does bar stuff c|comp|complete - complete stuff other - other stuff p|print - prints stuff bar - does bar stuff c|comp|complete - complete stuff other - other stuff p|print - prints stuff bar - does bar stuff c|comp|complete - complete stuff other - other stuff p|print - prints stuff
func (*Command) UnexpectedArg ΒΆ added in v0.6.3
UnexpectedArg returns cmdbox.UnexpectedArg
func (*Command) Unimplemented ΒΆ
Unimplemented is a convenience method that delegates calls to cmdbox.Unimplemented.
Example ΒΆ
package main import ( "fmt" "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing x := cmdbox.NewCommand("foo") fmt.Println(x.Unimplemented("help")) }
Output: unimplemented: help
func (*Command) UpdateUsage ΒΆ added in v0.4.0
func (x *Command) UpdateUsage()
UpdateUsage will set x.Usage to the default, which is all of the Commands joined with bar (|) and wrapped in either brackets ([]) or parenthesis (()) depending on whether a x.Default has been set or the command has it's own Method (effectively the default).
func (*Command) UsageError ΒΆ
UsageError is a convenience method that delegates calls to cmdbox.UsageError.
Example ΒΆ
package main import ( "fmt" "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing x := cmdbox.NewCommand("foo") x.Usage = "unique usage here" fmt.Println(x.UsageError()) }
Output: usage: foo unique usage here
func (*Command) YAML ΒΆ added in v0.1.0
YAML is shortcut for yaml.Marshal(x). See util.ToYAML.
Example ΒΆ
package main import ( "fmt" "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing x := cmdbox.NewCommand("foo") fmt.Print(x.YAML()) x.Print() }
Output: name: foo commands: {} default: help name: foo commands: {} default: help
type CommandMap ΒΆ added in v0.0.18
CommandMap encapsulates a map[string]*Command and embeds a sync.Mutex for locking making it safe for concurrency. The internal map is exported (M) in the event developers want more direct control without using the CommandMap methods that ensure concurrency safety. See the Register function for more.
func NewCommandMap ΒΆ added in v0.3.0
func NewCommandMap() *CommandMap
NewCommandMap returns a new CommandMap with the internal map[string]*Command initialized.
func (*CommandMap) Delete ΒΆ added in v0.3.0
func (m *CommandMap) Delete(keys ...string)
Delete removes one or more entries from the map in a way that is safe for concurrency.
func (CommandMap) Dups ΒΆ added in v0.3.0
func (m CommandMap) Dups() []string
Dups returns key strings of duplicates (which can then be easily renamed). Keys are sorted in lexicographic order. See Rename.
func (*CommandMap) Get ΒΆ added in v0.3.0
func (m *CommandMap) Get(key string) *Command
Get returns a Command pointer by key name safe for concurrency. Returns nil if not found.
func (*CommandMap) Init ΒΆ added in v0.3.0
func (m *CommandMap) Init()
Init initializes (or re-initialized) the CommandMap deleting all its values (without changing its reference).
func (CommandMap) JSON ΒΆ added in v0.3.0
func (m CommandMap) JSON() string
JSON is shortcut for json.Marshal(m). See ToJSON.
func (CommandMap) Names ΒΆ added in v0.0.18
func (m CommandMap) Names() []string
Names returns a sorted list of all Command names.
func (CommandMap) Print ΒΆ added in v0.3.0
func (m CommandMap) Print()
Print outputs as YAML (nice when testing).
func (CommandMap) Rename ΒΆ added in v0.3.0
func (m CommandMap) Rename(from, to string)
Rename renames a Command in the Register by adding the new name with the same *Command and deleting the old one. This is useful when a name conflict causes New to append and underscore (_) to the duplicate's name. Rename can be called from init() at any point after the duplicate has been added to resolve the conflict. Note the order of init() execution --- while predictable --- is not always apparent. When in doubt do Rename from main() to be sure. Rename is safe for concurrency.
func (*CommandMap) Set ΒΆ added in v0.3.0
func (m *CommandMap) Set(key string, val *Command)
Set set a value by key name safe in a way that is for concurrency.
func (CommandMap) Slice ΒΆ added in v0.3.0
func (m CommandMap) Slice(names ...string) []*Command
Slice returns a slice of *Command pointers and fetched from the internal register that match the key names passed. If an entry is not found it is simply skipped. Will return an empty slice if none found.
func (CommandMap) String ΒΆ added in v0.0.18
func (m CommandMap) String() string
String fullfills fmt.Stringer interface as JSON.
func (CommandMap) YAML ΒΆ added in v0.3.0
func (m CommandMap) YAML() string
YAML is shortcut for yaml.Marshal(m). See ToYAML.
type CompFunc ΒΆ added in v0.0.8
CompFunc defines a first-class function type for tab completion closures that sense the completion context, and return a list of completion strings. By managing completion logic as first-class functions we allow for easier completion testing and modularity. An empty string slice must always be returned even on failure.
type Method ΒΆ added in v0.1.0
Method represents a function to be used as Command.Method values. By convention, use "args" when arguments are expected and "none" when not.
func Resolve ΒΆ added in v0.1.6
Resolve looks up a Command from the internal Reg register based on the caller and the name. If the Name of the caller and name passed, joined with a space (a fully qualified entry) is found then that is used instead of just the name. Otherwise, just the name is looked up (which might itself already be fully qualified). The returned Command (x) is examined further to decide which Method and Args to return:
If x.Method defined, call and return it with args unaltered
If first arg in x.Commands, recursively Call with shifted args
First with x.Name + " " + cmd
Then with just cmd
If x.Default defined, recursively Call with shifted args
First with x.Name + " " + x.Default
Then with just x.Default
Return nil and args
By convention, passing a nil as the caller indicates the Command was called from something besides another Command, usually the cmdbox package itself or test cases. See Call, Command, ExampleResolve for more.
Example ΒΆ
package main import ( "fmt" "github.com/rwxrob/cmdbox" ) func main() { cmdbox.Init() // just for testing defer cmdbox.TrapPanicNoExit() gr := cmdbox.Add("greet", "fr|french", "ru|russian") gr.Summary = "main greet composite, no method" fr := cmdbox.Add("greet french") fr.Method = func(args ...string) error { fmt.Print("Salut") return nil } ru := cmdbox.Add("greet russian") ru.Method = func(args ...string) error { fmt.Print("Privyet") return nil } tests := []struct { caller *cmdbox.Command name string args []string }{ {nil, "greet", nil}, // usage {nil, "greet", []string{"h"}}, // usage {nil, "greet", []string{"hi"}}, // usage {nil, "greet russian", []string{"hi"}}, {gr, "russian", []string{"hi"}}, } for _, t := range tests { method, args := cmdbox.Resolve(t.caller, t.name, t.args) if method != nil { method(args...) fmt.Printf("%v %q\n", t.name, args) continue } fmt.Printf("failed: %v %q\n", t.name, t.args) } }
Output: failed: greet [] failed: greet ["h"] failed: greet ["hi"] Privyetgreet russian ["hi"] Privyetrussian ["hi"]
Directories
ΒΆ
Path | Synopsis |
---|---|
Package comp is the Bash tab completion subpackage of CmdBox.
|
Package comp is the Bash tab completion subpackage of CmdBox. |
Package complib is the tab completion function library for CmdBox.
|
Package complib is the tab completion function library for CmdBox. |
Package util contains utilities used by CmdBox itself or that are frequently needed and useful when writing Command.Method implementations.
|
Package util contains utilities used by CmdBox itself or that are frequently needed and useful when writing Command.Method implementations. |
The valid package simply and efficiently minimally validates command * line components passed into CmdBox commands, aliases, and such.
|
The valid package simply and efficiently minimally validates command * line components passed into CmdBox commands, aliases, and such. |