Documentation
¶
Overview ¶
Package cmdr provides an organized way to implement command line tools, with predictable behavior, arbitrary sub-commands and symlink-based multiplexing.
Internationalization (i18n) is supported through the Translator interface, configured with the WithTranslator option: usage output and error messages then render translated. Errors that implement the LocalizedError interface render themselves for the context of the failed run. SignalError, the cancellation cause RunMain records when a signal arrives, is one of them.
Flag defaults are a special case: flag.FlagSet.PrintDefaults hardcodes its "(default ...)" hint in English, with no i18n hook of its own. Commands that render usage through flagcmd.HelperContext should call flagcmd.PrintDefaults(ctx, fl) instead of fl.PrintDefaults() to keep that hint translated; calling fl.PrintDefaults() directly always renders it in English regardless of the installed Translator.
Index ¶
- Constants
- func CauseSignal(ctx context.Context) os.Signal
- func Name(ctx context.Context) (name string)
- func RunMain(cmd Cmd, args []string, opts ...Option)
- func RunSubCommand(ctx context.Context, cmd Cmd, name string, args []string) error
- func Stdio(ctx context.Context) (stdin io.Reader, stdout, stderr io.Writer)
- func Tr(ctx context.Context, msgid string) string
- func TrCtx(ctx context.Context, msgctxt, msgid string) string
- func TrCtxN(ctx context.Context, msgctxt, msgid, msgidPlural string, n int) string
- func TrN(ctx context.Context, msgid, msgidPlural string, n int) string
- func WithStdio(parent context.Context, stdin io.Reader, stdout, stderr io.Writer) context.Context
- type Args0Observer
- type Cmd
- type ExitCoder
- type ExitMessenger
- type Func
- type LocalizedError
- type Option
- type PanicBehavior
- type SignalError
- type SilentError
- type Translator
- type UnexpectedArgumentsError
Examples ¶
Constants ¶
const ExeSuffix = ""
ExeSuffix is the suffix for executable programs.
Variables ¶
This section is empty.
Functions ¶
func CauseSignal ¶ added in v1.17.0
CauseSignal returns the signal that cancelled ctx, or nil when ctx was not cancelled by a signal. It recognizes the cause RunMain records, SignalError, also when wrapped.
func Name ¶ added in v1.5.0
Name returns the name of the command.
Name always returns the accurate name of the executing command, which may be a nested name, for as long as RunMain and router.Cmd are used.
If the context is handled incorrectly, Name panics.
func RunMain ¶
RunMain runs the given command with arguments.
The context provided to the command is cancelled when os.Interrupt arrives, allowing commands to gracefully terminate on the interrupt signal. Other signals may be intercepted by passing WithSignals option. The cancellation cause is a SignalError naming the signal received; see CauseSignal.
If cmd has PassArgv0() bool method which returns true, then args[0] is preserved. This allows implementing symlink-based command multiplexers, such as Router.
If the command panics, the panic is recovered and an error message is printed. Other behaviors can be selected by passing WithPanicBehavior option. If the command returns an error, the error is printed, unless it is equal to flag.ErrHelp.
The exit code from the process is non-zero when the command returns an error or causes a panic. Errors implementing the ExitCoder interface can provide the exit code explicitly. The special flag.ErrHelp is translated to EX_USAGE (64). All other errors set exit code to one.
The standard output and error streams are always synchronized before terminating the process.
Example ¶
You can structure your application as a number of command types that get invoked at the right time. All you need is a `Run(context.Context, []string) error` function on your types. Inside that function, you can do anything.
package main
import (
"context"
"fmt"
"gitlab.com/zygoon/go-cmdr"
)
type helloCmd struct{}
func (helloCmd) Run(context.Context, []string) error {
fmt.Println("Hello World")
return nil
}
func main() {
cmdr.RunMain(helloCmd{}, []string{"example"})
}
Output: Hello World
func RunSubCommand ¶ added in v1.7.0
RunSubCommand runs a given command as a sub-command.
This function allows replicating all the router functionality in a custom type, perhaps one with logic beyond the capabilities of the default router.
Unlike just running the command directly, the invoked sub-command can access the correctly nested sub-command name through cmdr.Name.
func Stdio ¶ added in v1.1.0
Stdio returns the standard IO streams for the given context.
If the context was returned by WithStdio, the encoded streams are recovered. In all other cases os.{Stdin,Stdout,Stderr} is returned.
IO streams may be replaced for testing. Recovering the streams from the context avoids the need to use and manage per-command global variables.
Example ¶
Apart from serving the usual role, the context may be used to obtain the three standard input and output streams. The returned streams are os.Stdin, os.Stdout and os.Stderr. In unit tests, those may be redirected to buffers to aid in testing the output produced by commands. This is especially useful and easy with the cmdtest package.
package main
import (
"context"
"fmt"
"log"
"gitlab.com/zygoon/go-cmdr"
"gitlab.com/zygoon/go-cmdr/cmdtest"
)
func main() {
cmd := cmdr.Func(func(ctx context.Context, args []string) error {
_, stdout, _ := cmdr.Stdio(ctx)
_, _ = fmt.Fprintln(stdout, "Hello World")
return nil
})
inv := cmdtest.Invoke(cmd)
if err := inv.ExpectStdout("Hello World\n"); err != nil {
log.Fatal(err)
}
// This produces no output as the output is captured the invocation helper
// and is available for tests to inspect.
}
Output:
func Tr ¶ added in v1.14.0
Tr translates msgid through the Translator carried by ctx — the one configured with WithTranslator. Without a translator the msgid is returned as-is. Commands use it for their own user-facing strings, with the same context they were run with.
func TrCtx ¶ added in v1.16.0
TrCtx translates msgid through the Translator carried by ctx, the same as Tr, but disambiguated by msgctxt: use it for short or generic msgids (e.g. "Version") that read differently in different places and would otherwise collide in the catalog. Without a translator the msgid is returned as-is.
func TrCtxN ¶ added in v1.16.0
TrCtxN combines TrCtx and TrN: translates a singular/plural message pair selected by n, disambiguated by msgctxt. Without a translator, English selection is used: msgid when n == 1, msgidPlural otherwise.
func TrN ¶ added in v1.14.0
TrN translates a singular/plural message pair selected by n through the Translator carried by ctx. Without a translator, English selection is used: msgid when n == 1, msgidPlural otherwise.
Types ¶
type Args0Observer ¶
type Args0Observer interface {
PreserveArgs0() bool
}
Args0Observer instructs RunMain to preserve args[0].
type Cmd ¶
type Cmd interface {
// Run runs the command with the given context and arguments.
// The context can be used to pass global options to a command.
Run(ctx context.Context, args []string) error
}
Cmd is a runnable command.
type ExitCoder ¶
type ExitCoder interface {
ExitCode() int
}
ExitCoder is an interface for errors to convey a process exit code.
type ExitMessenger ¶ added in v1.6.0
type ExitMessenger interface {
ExitMessage() string
}
ExitMessenger is an interface for errors to convey a specific error message.
ExitMessenger may be used to suppress the system that automatically prints the error value if the error message returned is empty.
type Func ¶
Func is the type of functions runnable as commands.
Example ¶
The most basic example uses `cmdr.RunMain` to run a function that takes a context, arguments and returns an error. The context has multiple uses as we will see later. The `cmdr.Func` type adapts a function to `cmdr.Cmd` interface. In essence, anything that can be called with string arguments is runnable as a command.
package main
import (
"context"
"fmt"
"gitlab.com/zygoon/go-cmdr"
)
func main() {
cmd := cmdr.Func(func(ctx context.Context, args []string) error {
fmt.Println("Hello World")
return nil
})
cmdr.RunMain(cmd, []string{"example"})
}
Output: Hello World
type LocalizedError ¶ added in v1.14.0
LocalizedError is the interface of errors that can render themselves for the context of the failed run — typically translated by an i18n library. It is detected structurally with errors.As, so error types from any library satisfy it without go-cmdr depending on one.
type Option ¶ added in v1.2.0
type Option func(*mainOpts)
Option influences how RunMain operates.
func WithArgv0 ¶ added in v1.2.0
WithArgv0 sets the name of the main command.
RunMain occasionally prints the name of the main command. The name of the main command is either "?" or the base name of os.Args[0]. This option allows using a custom name.
func WithBaseContext ¶ added in v1.2.0
WithBaseContext sets the base of the context provided to the main command.
RunMain provides a context to executed command. This context defaults to a context derived from context.Background. This option allows using a custom base context, for example, a context with a deadline.
func WithExitFunc ¶ added in v1.2.1
WithExitFunc overrides the default os.Exit with a custom function.
Note that the exit function when a non-zero needs to be returned. On successful exit or when the error maps to an exit code of zero, the exit function is not called.
func WithPanicBehavior ¶ added in v1.2.0
func WithPanicBehavior(b PanicBehavior) Option
WithPanicBehavior configures panic behavior.
RunMain recovers any panic that happens during execution of the main command. By default, only the panic message is printed. This option allows selecting other behavior.
func WithSignals ¶ added in v1.2.0
WithSignals sets signals canceling the context passed to the main command.
RunMain cancels the context when one of the signals arrives, recording a SignalError as the cancellation cause. By default, os.Interrupt is intercepted. This option allows using a different set of signals.
func WithTranslator ¶ added in v1.14.0
func WithTranslator(tr Translator) Option
WithTranslator sets the translator for internationalization.
RunMain uses the translator to render translated usage output and error messages, and commands can reach it through cmdr.Tr and cmdr.TrN with the context they were run with. If no translator is set, message IDs are used as-is.
The translator must resolve msgids from go-cmdr's own catalog (router headers, stock command one-liners, built-in error messages) as well as the application's own — go-cmdr's rendering code and the application's commands read back the same context-carried translator through cmdr.Tr/cmdr.TrN. A gettext-style i18n library typically builds this by binding the application's domain together with go-cmdr's own.
type PanicBehavior ¶ added in v1.2.0
type PanicBehavior int
PanicBehavior controls reaction to panic encountered in RunMain.
Use WithPanicBehavior to
const ( // RecoverExit is the default panic behavior of RunMain. // // Panic caused by command execution is recovered. The recovered value // is printed to standard error. Program execution is terminated with // os.Exit, or the configured exit function, with code 70. RecoverExit PanicBehavior = iota // RecoverTraceExit prints a stack trace on panic. // // RecoverStackTrace is like RecoverExit in the sense that a panic is // both recovered and that the process terminates with an exit code. In // addition, the stack trace of the go routine which has caused the // panic is printed to standard error. RecoverTraceExit )
type SignalError ¶ added in v1.17.0
SignalError is the cancellation cause RunMain records when one of the signals selected with WithSignals arrives. Unlike the cause recorded by signal.NotifyContext, it keeps the signal itself, so commands can tell which signal ended the run without matching on error text.
SignalError matches context.Canceled with errors.Is, and its Error text is the same as the one signal.NotifyContext uses.
func (SignalError) Error ¶ added in v1.17.0
func (e SignalError) Error() string
Error returns an error message naming the received signal.
func (SignalError) Is ¶ added in v1.17.0
func (e SignalError) Is(target error) bool
Is reports whether target is context.Canceled.
func (SignalError) LocalizedError ¶ added in v1.17.0
func (e SignalError) LocalizedError(ctx context.Context) string
LocalizedError renders the message through the Translator carried by ctx. The signal name is translated as well, for the signals go-cmdr knows by name; others keep the name the operating system gives them.
type SilentError ¶ added in v1.6.0
type SilentError uint8
SilentError is an error type that produces a given error code but no error message.
func (SilentError) Error ¶ added in v1.6.0
func (e SilentError) Error() string
Error returns the empty string.
func (SilentError) ExitCode ¶ added in v1.6.0
func (e SilentError) ExitCode() int
ExitCode returns the integer encoded in the error.
func (SilentError) ExitMessage ¶ added in v1.6.0
func (e SilentError) ExitMessage() string
ExitMessage returns the empty string.
cmdr.RunMain suppresses error output when ExitMessage is implemented by the error value and the value is the empty string.
type Translator ¶ added in v1.14.0
type Translator = internal.Translator
Translator is the interface for internationalization similar to gettext. Implement this interface to provide translation for error messages and other user-facing strings.
type UnexpectedArgumentsError ¶ added in v1.10.0
type UnexpectedArgumentsError []string
UnexpectedArgumentsError records arguments that were not anticipated by a command.
func (UnexpectedArgumentsError) Error ¶ added in v1.10.0
func (e UnexpectedArgumentsError) Error() string
Error returns an error message describing unexpected arguments.
func (UnexpectedArgumentsError) LocalizedError ¶ added in v1.14.0
func (e UnexpectedArgumentsError) LocalizedError(ctx context.Context) string
LocalizedError renders the message through the Translator carried by ctx. The message is composed with Sprintf, so unlike plain sentinel errors it can never translate by exact text; implementing the LocalizedError interface lets RunMain's error printer render it.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package cmdtest contains utilities for testing commands.
|
Package cmdtest contains utilities for testing commands. |
|
internal/install
Package install exists to provide regression tests against https://gitlab.com/zygoon/go-cmdr/-/issues/4
|
Package install exists to provide regression tests against https://gitlab.com/zygoon/go-cmdr/-/issues/4 |
|
Package flagcmd implements extensible commands based on standard library flag.FlagSet argument parser.
|
Package flagcmd implements extensible commands based on standard library flag.FlagSet argument parser. |
|
Package format implements the output format hint system.
|
Package format implements the output format hint system. |
|
jsonformat
Package jsonformat implements support for outputting JSON.
|
Package jsonformat implements support for outputting JSON. |
|
Package l10n ships go-cmdr's translation catalogs as data — .po files behind an embed.FS — with no dependency on any translation library.
|
Package l10n ships go-cmdr's translation catalogs as data — .po files behind an embed.FS — with no dependency on any translation library. |
|
Package router provides command router useful for creating arbitrary sub-command trees.
|
Package router provides command router useful for creating arbitrary sub-command trees. |
|
samples
module
|
|
|
Package stockcmds contains stock commands that are applicable to many programs.
|
Package stockcmds contains stock commands that are applicable to many programs. |