Documentation
¶
Overview ¶
Package acmd is a simple, useful and opinionated CLI package in Go.
See: https://github.com/cristalhq/acmd
Example ¶
package main import ( "context" "flag" "fmt" "net/http" "os" "time" "github.com/cristalhq/acmd" ) var nopUsage = func(cfg acmd.Config, cmds []acmd.Command) {} func main() { testOut := os.Stdout testArgs := []string{"someapp", "now", "--times", "3"} const format = "15:04:05" now, _ := time.Parse(format, "10:20:30") cmds := []acmd.Command{ { Name: "now", Description: "prints current time", ExecFunc: func(ctx context.Context, args []string) error { fs := flag.NewFlagSet("some name for help", flag.ContinueOnError) times := fs.Int("times", 1, "how many times to print time") if err := fs.Parse(args); err != nil { return err } for i := 0; i < *times; i++ { fmt.Printf("now: %s\n", now.Format(format)) } return nil }, FlagSet: &commandFlags{}, }, { Name: "status", Description: "prints status of the system", ExecFunc: func(ctx context.Context, args []string) error { req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "https://www.githubstatus.com/", http.NoBody) resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close() // TODO: parse response, I don't know return nil }, }, } r := acmd.RunnerOf(cmds, acmd.Config{ AppName: "acmd-example", AppDescription: "Example of acmd package", PostDescription: "Best place to add examples", Version: "the best v0.x.y", Output: testOut, Args: testArgs, Usage: nopUsage, }) if err := r.Run(); err != nil { panic(err) } } type generalFlags struct { IsVerbose bool Dir string } func (c *generalFlags) Flags() *flag.FlagSet { fs := flag.NewFlagSet("", flag.ContinueOnError) fs.BoolVar(&c.IsVerbose, "verbose", false, "should app be verbose") fs.StringVar(&c.Dir, "dir", ".", "directory to process") return fs } type commandFlags struct { generalFlags File string } func (c *commandFlags) Flags() *flag.FlagSet { fs := c.generalFlags.Flags() fs.StringVar(&c.File, "file", "input.txt", "file to process") return fs }
Output: now: 10:20:30 now: 10:20:30 now: 10:20:30
Example (Alias) ¶
package main import ( "context" "fmt" "os" "github.com/cristalhq/acmd" ) var nopUsage = func(cfg acmd.Config, cmds []acmd.Command) {} func main() { testOut := os.Stdout testArgs := []string{"someapp", "f"} cmds := []acmd.Command{ { Name: "foo", Alias: "f", ExecFunc: func(ctx context.Context, args []string) error { fmt.Fprint(testOut, "foo") return nil }, }, { Name: "bar", Alias: "b", ExecFunc: func(ctx context.Context, args []string) error { fmt.Fprint(testOut, "bar") return nil }, }, } r := acmd.RunnerOf(cmds, acmd.Config{ AppName: "acmd-example", AppDescription: "Example of acmd package", Version: "the best v0.x.y", Output: testOut, Args: testArgs, Usage: nopUsage, }) if err := r.Run(); err != nil { panic(err) } }
Output: foo
Example (Autosuggestion) ¶
package main import ( "context" "os" "github.com/cristalhq/acmd" ) var ( nopFunc = func(context.Context, []string) error { return nil } nopUsage = func(cfg acmd.Config, cmds []acmd.Command) {} ) func main() { testOut := os.Stdout testArgs := []string{"someapp", "baz"} cmds := []acmd.Command{ {Name: "foo", ExecFunc: nopFunc}, {Name: "bar", ExecFunc: nopFunc}, } r := acmd.RunnerOf(cmds, acmd.Config{ AppName: "acmd-example", AppDescription: "Example of acmd package", Version: "the best v0.x.y", Output: testOut, Args: testArgs, Usage: nopUsage, }) if err := r.Run(); err == nil { panic("must fail with command not found") } }
Output: "baz" unknown command, did you mean "bar"? Run "acmd-example help" for usage.
Example (ExecStruct) ¶
package main import ( "context" "errors" "fmt" "io" "github.com/cristalhq/acmd" ) var nopUsage = func(cfg acmd.Config, cmds []acmd.Command) {} type myCommand struct { ErrToReturn error } func (mc *myCommand) ExecCommand(ctx context.Context, args []string) error { return mc.ErrToReturn } func main() { myErr := errors.New("everything is ok") myCmd := &myCommand{ErrToReturn: myErr} cmds := []acmd.Command{ { Name: "what", Description: "does something", // ExecFunc: myCmd.ExecCommand, // NOTE: line below is literally line above Exec: myCmd, }, } r := acmd.RunnerOf(cmds, acmd.Config{ AppName: "acmd-example", AppDescription: "Example of acmd package", PostDescription: "Best place to add examples", Output: io.Discard, Args: []string{"someapp", "what"}, Usage: nopUsage, }) err := r.Run() if !errors.Is(err, myErr) { panic(fmt.Sprintf("\ngot : %+v\nwant: %+v\n", err, myErr)) } }
Output:
Example (NestedCommands) ¶
package main import ( "context" "fmt" "os" "github.com/cristalhq/acmd" ) var nopFunc = func(context.Context, []string) error { return nil } func main() { testOut := os.Stdout testArgs := []string{"someapp", "foo", "qux"} cmds := []acmd.Command{ { Name: "foo", Subcommands: []acmd.Command{ {Name: "bar", ExecFunc: nopFunc}, {Name: "baz", ExecFunc: nopFunc}, { Name: "qux", ExecFunc: func(ctx context.Context, args []string) error { fmt.Fprint(testOut, "qux") return nil }, }, }, }, {Name: "boom", ExecFunc: nopFunc}, } r := acmd.RunnerOf(cmds, acmd.Config{ AppName: "acmd-example", AppDescription: "Example of acmd package", Version: "the best v0.x.y", Output: testOut, Args: testArgs, }) if err := r.Run(); err != nil { panic(err) } }
Output: qux
Example (PropagateFlags) ¶
package main import ( "bytes" "context" "flag" "fmt" "os" "github.com/cristalhq/acmd" ) func main() { testOut := os.Stdout testArgs := []string{"someapp", "foo", "-dir=test-dir", "--verbose"} buf := &bytes.Buffer{} cmds := []acmd.Command{ { Name: "foo", ExecFunc: func(ctx context.Context, args []string) error { var cfg generalFlags if err := cfg.Flags().Parse(args); err != nil { return err } if cfg.IsVerbose { fmt.Fprintf(buf, "TODO: dir %q, is verbose = %v\n", cfg.Dir, cfg.IsVerbose) } return nil }, }, { Name: "bar", ExecFunc: func(ctx context.Context, args []string) error { var cfg commandFlags if err := cfg.Flags().Parse(args); err != nil { return err } if cfg.IsVerbose { fmt.Fprintf(buf, "TODO: dir %q\n", cfg.Dir) } return nil }, }, } r := acmd.RunnerOf(cmds, acmd.Config{ AppName: "acmd-example", AppDescription: "Example of acmd package", Version: "the best v0.x.y", Output: testOut, Args: testArgs, }) if err := r.Run(); err != nil { panic(err) } fmt.Println(buf.String()) } type generalFlags struct { IsVerbose bool Dir string } func (c *generalFlags) Flags() *flag.FlagSet { fs := flag.NewFlagSet("", flag.ContinueOnError) fs.BoolVar(&c.IsVerbose, "verbose", false, "should app be verbose") fs.StringVar(&c.Dir, "dir", ".", "directory to process") return fs } type commandFlags struct { generalFlags File string } func (c *commandFlags) Flags() *flag.FlagSet { fs := c.generalFlags.Flags() fs.StringVar(&c.File, "file", "input.txt", "file to process") return fs }
Output: TODO: dir "test-dir", is verbose = true
Example (VerboseHelp) ¶
package main import ( "context" "flag" "os" "github.com/cristalhq/acmd" ) var nopFunc = func(context.Context, []string) error { return nil } func main() { testOut := os.Stdout testArgs := []string{"someapp", "help"} cmds := []acmd.Command{ { Name: "now", Description: "prints current time", ExecFunc: nopFunc, }, { Name: "status", Description: "prints status of the system", ExecFunc: nopFunc, }, { Name: "boom", ExecFunc: nopFunc, FlagSet: &generalFlags{}, }, { Name: "time", Subcommands: []acmd.Command{ {Name: "next", ExecFunc: nopFunc, Description: "next time subcommand"}, {Name: "curr", ExecFunc: nopFunc, Description: "curr time subcommand"}, }, }, } r := acmd.RunnerOf(cmds, acmd.Config{ AppName: "acmd-example", AppDescription: "Example of acmd package", PostDescription: "Best place to add examples.", Version: "the best v0.x.y", Output: testOut, Args: testArgs, VerboseHelp: !true, // TODO(cristaloleg): fix this }) if err := r.Run(); err != nil { panic(err) } } type generalFlags struct { IsVerbose bool Dir string } func (c *generalFlags) Flags() *flag.FlagSet { fs := flag.NewFlagSet("", flag.ContinueOnError) fs.BoolVar(&c.IsVerbose, "verbose", false, "should app be verbose") fs.StringVar(&c.Dir, "dir", ".", "directory to process") return fs }
Output: Example of acmd package Usage: acmd-example <command> [arguments...] The commands are: boom <no description> help shows help message now prints current time status prints status of the system time curr curr time subcommand time next next time subcommand version shows version of the application Best place to add examples. Version: the best v0.x.y
Example (Version) ¶
package main import ( "context" "os" "github.com/cristalhq/acmd" ) var ( nopFunc = func(context.Context, []string) error { return nil } nopUsage = func(cfg acmd.Config, cmds []acmd.Command) {} ) func main() { testOut := os.Stdout testArgs := []string{"someapp", "version"} cmds := []acmd.Command{ {Name: "foo", ExecFunc: nopFunc}, {Name: "bar", ExecFunc: nopFunc}, } r := acmd.RunnerOf(cmds, acmd.Config{ AppName: "acmd-example", AppDescription: "Example of acmd package", Version: "the best v0.x.y", Output: testOut, Args: testArgs, Usage: nopUsage, }) if err := r.Run(); err != nil { panic(err) } }
Output: acmd-example version: the best v0.x.y
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var ErrNoArgs = errors.New("no args provided")
Functions ¶
func AutocompleteFor ¶ added in v0.5.4
func HasHelpFlag ¶ added in v0.5.1
HasHelpFlag reports whether help flag is presented in args.
Types ¶
type Command ¶
type Command struct { // Name of the command, ex: `init` Name string // Alias is an optional short second name, ex: `i`. Alias string // Description of the command. Description string // ExecFunc represents the command function. // Use Exec if you have struct implementing this function. ExecFunc func(ctx context.Context, args []string) error // Exec represents the command function. // Will be used only if ExecFunc is nil. Exec Exec // Subcommands of the command. Subcommands []Command // IsHidden reports whether command should not be show in help. Default false. IsHidden bool // FlagSet is an optional field where you can provide command's flags. // Is used for autocomplete. Works best with https://github.com/cristalhq/flagx FlagSet FlagsGetter }
Command specifies a sub-command for a program's command-line interface.
type Config ¶
type Config struct { // AppName is an optional name for the app, if empty os.Args[0] will be used. AppName string // AppDescription is an optional description. default is empty. AppDescription string // PostDescription of the command. Is shown after a help. PostDescription string // Version of the application. Version string // Output is a destination where result will be printed. // Exported for testing purpose only, if nil os.Stdout is used. Output io.Writer // Context for commands, if nil context based on os.Interrupt and syscall.SIGTERM will be used. Context context.Context // Args passed to the executable, if nil os.Args[1:] will be used. Args []string // Usage of the application, if nil default will be used. Usage func(cfg Config, cmds []Command) // VerboseHelp if "./app help -v" is passed, default is false. VerboseHelp bool // AllowNoCommands set to true will not panic on empty list of commands. AllowNoCommands bool // contains filtered or unexported fields }
Config for the runner.
type FlagsGetter ¶ added in v0.9.1
FlagsGetter returns flags for the command. See examples.
type Runner ¶
type Runner struct {
// contains filtered or unexported fields
}
Runner of the sub-commands.
Click to show internal directories.
Click to hide internal directories.