diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5f0ef30..2f2112a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,7 +12,7 @@ on: jobs: test: name: Test - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 diff --git a/README.md b/README.md index 7e8b776..b7eb9f3 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,14 @@ # broccli -[![Go Reference](https://pkg.go.dev/badge/gopkg.pl/phings/broccli/v2.svg)](https://pkg.go.dev/gopkg.pl/phings/broccli/v2) [![Go Report Card](https://goreportcard.com/badge/gopkg.pl/phings/broccli/v2)](https://goreportcard.com/report/gopkg.pl/phings/broccli/v2) +[![Go Reference](https://pkg.go.dev/badge/gopkg.pl/phings/broccli/v3.svg)](https://pkg.go.dev/gopkg.pl/phings/broccli/v3) [![Go Report Card](https://goreportcard.com/badge/gopkg.pl/phings/broccli/v3)](https://goreportcard.com/report/gopkg.pl/phings/broccli/v3) ---- -The `phings/broccli` package simplifies command line interface management. It allows you to define commands complete with arguments and flags, and attach handlers to them. The package handles all the parsing automatically. +The `phings/broccli/v3` package simplifies command line interface management. It allows you to define commands complete with arguments and flags, and attach handlers to them. The package handles all the parsing automatically. + + +:warning: `v3` is not compatible with `v2`. The latest version requires a context in handlers and when calling `Run` method. Also +methods for adding commands, args etc. have been shortened to `Command`, `Arg`, `Flag` and `Env`. ---- @@ -26,17 +30,17 @@ However, the following code snippet from another tiny project shows how the modu Import the code with the following URL: ```go -import "gopkg.pl/phings/broccli/v2" +import "gopkg.pl/phings/broccli/v3" ``` ```go // create new CLI object -cli := broccli.NewCLI("snakey-letters", "Classic snake but with letters and words!", "") +cli := broccli.NewBroccli("snakey-letters", "Classic snake but with letters and words!", "") // add a command and attach a function to it -cmd := cli.AddCmd("start", "Starts the game", startHandler) +cmd := cli.Command("start", "Starts the game", startHandler) // add a flag to the command -cmd.AddFlag("words", "f", "", +cmd.Flag("words", "f", "", "Text file with wordlist", // should be a path to a file broccli.TypePathFile, @@ -45,39 +49,39 @@ cmd.AddFlag("words", "f", "", ) // add another command to print version number -_ = cli.AddCmd("version", "Shows version", versionHandler) +_ = cli.Command("version", "Shows version", versionHandler) // run cli -os.Exit(cli.Run()) +os.Exit(cli.Run(context.Background())) // handlers for each command -func startHandler(c *broccli.CLI) int { +func startHandler(ctx context.Context, c *broccli.CLI) int { fmt.Fprint(os.Stdout, "Starting with file %s...", c.Flag("words")) return 0 } -func versionHandler(c *broccli.CLI) int { +func versionHandler(ctx context.Context, c *broccli.CLI) int { fmt.Fprintf(os.Stdout, VERSION+"\n") return 0 } ``` ## Structs explained -### CLI -The main `CLI` object has three arguments such as name, description and author. These guys are displayed when syntax is printed out. +### Broccli +The main `Broccli` object has three arguments such as name, usage and author. These guys are displayed when syntax is printed out. ### Commands Method `AddCmd` creates a new command which has the following properties. * a `name`, used to call it -* short `description` - few words to display what the command does on the syntax screen +* short `usage` - few words to display what the command does on the syntax screen * `handler` function that is executed when the command is called and all its flags and argument are valid #### Command Options Optionally, after command flags and arguments are successfully validated, and just before the execution of `handler`, additional code (func) can be executed. This can be passed as a last argument. ```go -cmd := cli.AddCmd("start", "Starts the game", startHandler, +cmd := cli.Command("start", "Starts the game", startHandler, broccli.OnPostValidation(func(c *broccli.Cmd) { // do something, even with the command }), @@ -97,16 +101,16 @@ To setup a flag in a command, method `AddFlag` is used. It takes the following a * `name` and `alias` that are used to call the flag (eg. `--help` and `-h`, without the hyphens in the func args) * `valuePlaceholder`, a placeholder that is printed out on the syntax screen, eg. in `-f PATH_TO_FILE` it is the `PATH_TO_FILE` -* `description` - few words telling what the command does (syntax screen again) +* `usage` - few words telling what the command does (syntax screen again) * `types`, an int64 value that defines the value type, currently one of `TypeString`, `TypeBool`, `TypeInt`, `TypeFloat`, `TypeAlphanumeric` or `TypePathFile` (see `flags.go` for more information) * `flags`, an int64 value containing validation requirement, eg. `IsRequired|IsExistent|IsDirectory` could be used with `TypePathFile` to require the flag to be a non-empty path to an existing directory (again, navigate to `flags.go` for more detailed information) Optionally, a function can be attached to a boolean flag that is triggered when a flag is true. The motivation behind that was a use case when setting a certain flag to true would make another string flag required. However, it's not recommended to be used. -To add an argument for a command, method `AddArg` shall be used. It has almost the same arguments, apart from the fact that `alias` is not there. +To add an argument for a command, method `Arg` shall be used. It has almost the same arguments, apart from the fact that `alias` is not there. ### Environment variables to check -Command may require environment variables. `AddEnvVar` can be called to setup environment variables that should be verified before running the command. For example, a variable might need to contain a path to an existing regular file. +Command may require environment variables. `Env` can be called to setup environment variables that should be verified before running the command. For example, a variable might need to contain a path to an existing regular file. ### Accessing flag and arg values See sample code that does that below. @@ -129,3 +133,4 @@ func startHandler(c *broccli.CLI) int { - [X] Check for file existence and its type (can be directory, regular file or other) - [X] Post validation hook - [X] Boolean flag on-true hook before validation +- [X] Handlers require context diff --git a/cli.go b/cli.go index 390dcd1..d2c4d6f 100644 --- a/cli.go +++ b/cli.go @@ -1,9 +1,10 @@ package broccli import ( + "context" "flag" "fmt" - "io/ioutil" + "io" "os" "path" "reflect" @@ -11,96 +12,96 @@ import ( "text/tabwriter" ) -// CLI is main CLI application definition. +// Broccli is main CLI application definition. // It has a name, description, author which are printed out to the screen in the usage syntax. -// Each CLI have commands (represented by Cmd). Optionally, it is possible to require environment +// Each CLI have commands (represented by Command). Optionally, it is possible to require environment // variables. -type CLI struct { +type Broccli struct { name string - desc string + usage string author string - cmds map[string]*Cmd - envVars map[string]*param + commands map[string]*Command + env map[string]*param parsedFlags map[string]string parsedArgs map[string]string } -// NewCLI returns pointer to a new CLI instance. Name, description and author are displayed on the syntax screen. -func NewCLI(name string, description string, author string) *CLI { - c := &CLI{ +// NewBroccli returns pointer to a new Broccli instance. Name, usage and author are displayed on the syntax screen. +func NewBroccli(name, usage, author string) *Broccli { + c := &Broccli{ name: name, - desc: description, + usage: usage, author: author, - cmds: map[string]*Cmd{}, - envVars: map[string]*param{}, + commands: map[string]*Command{}, + env: map[string]*param{}, parsedFlags: map[string]string{}, parsedArgs: map[string]string{}, } return c } -// AddCmd returns pointer to a new command with specified name, description and handler. Handler is a function that +// Command returns pointer to a new command with specified name, usage and handler. Handler is a function that // gets called when command is executed. -// Additionally, there is a set of options that can be passed as arguments. Search for cmdOption for more info. -func (c *CLI) AddCmd(name string, description string, handler func(cli *CLI) int, opts ...cmdOption) *Cmd { - c.cmds[name] = &Cmd{ +// Additionally, there is a set of options that can be passed as arguments. Search for commandOption for more info. +func (c *Broccli) Command(name, usage string, handler func(ctx context.Context, cli *Broccli) int, opts ...commandOption) *Command { + c.commands[name] = &Command{ name: name, - desc: description, + usage: usage, flags: map[string]*param{}, args: map[string]*param{}, - envVars: map[string]*param{}, + env: map[string]*param{}, handler: handler, - options: cmdOptions{}, + options: commandOptions{}, } for _, o := range opts { - o(&(c.cmds[name].options)) + o(&(c.commands[name].options)) } - return c.cmds[name] + return c.commands[name] } -// AddEnvVar returns pointer to a new environment variable that is required to run every command. -// Method requires name, eg. MY_VAR, and description. -func (c *CLI) AddEnvVar(name string, description string) { - c.envVars[name] = ¶m{ +// Env returns pointer to a new environment variable that is required to run every command. +// Method requires name, eg. MY_VAR, and usage. +func (c *Broccli) Env(name string, usage string) { + c.env[name] = ¶m{ name: name, - desc: description, + usage: usage, flags: IsRequired, options: paramOptions{}, } } // Flag returns value of flag. -func (c *CLI) Flag(name string) string { +func (c *Broccli) Flag(name string) string { return c.parsedFlags[name] } // Arg returns value of arg. -func (c *CLI) Arg(name string) string { +func (c *Broccli) Arg(name string) string { return c.parsedArgs[name] } // Run parses the arguments, validates them and executes command handler. // In case of invalid arguments, error is printed to stderr and 1 is returned. Return value should be treated as exit // code. -func (c *CLI) Run() int { +func (c *Broccli) Run(ctx context.Context) int { // display help, first arg is binary filename if len(os.Args) < 2 || os.Args[1] == "-h" || os.Args[1] == "--help" { c.printHelp() return 0 } - for _, n := range c.sortedCmds() { + for _, n := range c.sortedCommands() { if n != os.Args[1] { continue } // display command help if len(os.Args) > 2 && (os.Args[2] == "-h" || os.Args[2] == "--help") { - c.cmds[n].printHelp() + c.commands[n].printHelp() return 0 } // check required environment variables - if len(c.envVars) > 0 { - for env, param := range c.envVars { + if len(c.env) > 0 { + for env, param := range c.env { v := os.Getenv(env) param.flags = param.flags | IsRequired err := param.validateValue(v) @@ -113,21 +114,21 @@ func (c *CLI) Run() int { } // parse and validate all the flags and args - exitCode := c.parseFlags(c.cmds[n]) + exitCode := c.parseFlags(c.commands[n]) if exitCode > 0 { return exitCode } - return c.cmds[n].handler(c) + return c.commands[n].handler(ctx, c) } // command not found - c.printInvalidCmd(os.Args[1]) + c.printInvalidCommand(os.Args[1]) return 1 } -func (c *CLI) sortedCmds() []string { - cmds := reflect.ValueOf(c.cmds).MapKeys() +func (c *Broccli) sortedCommands() []string { + cmds := reflect.ValueOf(c.commands).MapKeys() scmds := make([]string, len(cmds)) for i, cmd := range cmds { scmds[i] = cmd.String() @@ -136,8 +137,8 @@ func (c *CLI) sortedCmds() []string { return scmds } -func (c *CLI) sortedEnvVars() []string { - evs := reflect.ValueOf(c.envVars).MapKeys() +func (c *Broccli) sortedEnv() []string { + evs := reflect.ValueOf(c.env).MapKeys() sevs := make([]string, len(evs)) for i, ev := range evs { sevs[i] = ev.String() @@ -146,16 +147,16 @@ func (c *CLI) sortedEnvVars() []string { return sevs } -func (c *CLI) printHelp() { - fmt.Fprintf(os.Stdout, "%s by %s\n%s\n\n", c.name, c.author, c.desc) +func (c *Broccli) printHelp() { + fmt.Fprintf(os.Stdout, "%s by %s\n%s\n\n", c.name, c.author, c.usage) fmt.Fprintf(os.Stdout, "Usage: %s COMMAND\n\n", path.Base(os.Args[0])) - if len(c.envVars) > 0 { + if len(c.env) > 0 { fmt.Fprintf(os.Stdout, "Required environment variables:\n") w := new(tabwriter.Writer) w.Init(os.Stdout, 8, 8, 0, '\t', 0) - for _, n := range c.sortedEnvVars() { - fmt.Fprintf(w, "%s\t%s\n", n, c.envVars[n].desc) + for _, n := range c.sortedEnv() { + fmt.Fprintf(w, "%s\t%s\n", n, c.env[n].usage) } w.Flush() } @@ -163,25 +164,25 @@ func (c *CLI) printHelp() { fmt.Fprintf(os.Stdout, "Commands:\n") w := new(tabwriter.Writer) w.Init(os.Stdout, 10, 8, 0, '\t', 0) - for _, n := range c.sortedCmds() { - fmt.Fprintf(w, " %s\t%s\n", n, c.cmds[n].desc) + for _, n := range c.sortedCommands() { + fmt.Fprintf(w, " %s\t%s\n", n, c.commands[n].usage) } w.Flush() fmt.Fprintf(os.Stdout, "\nRun '%s COMMAND --help' for command syntax.\n", path.Base(os.Args[0])) } -func (c *CLI) printInvalidCmd(cmd string) { +func (c *Broccli) printInvalidCommand(cmd string) { fmt.Fprintf(os.Stderr, "Invalid command: %s\n\n", cmd) c.printHelp() } // getFlagSetPtrs creates flagset instance, parses flags and returns list of pointers to results of parsing the flags. -func (c *CLI) getFlagSetPtrs(cmd *Cmd) (map[string]interface{}, map[string]interface{}, []string) { +func (c *Broccli) getFlagSetPtrs(cmd *Command) (map[string]interface{}, map[string]interface{}, []string) { fset := flag.NewFlagSet("flagset", flag.ContinueOnError) // nothing should come out of flagset fset.Usage = func() {} - fset.SetOutput(ioutil.Discard) + fset.SetOutput(io.Discard) nameFlags := make(map[string]interface{}) aliasFlags := make(map[string]interface{}) @@ -200,12 +201,12 @@ func (c *CLI) getFlagSetPtrs(cmd *Cmd) (map[string]interface{}, map[string]inter return nameFlags, aliasFlags, fset.Args() } -func (c *CLI) checkEnvVars(cmd *Cmd) int { - if len(cmd.envVars) == 0 { +func (c *Broccli) checkEnv(cmd *Command) int { + if len(cmd.env) == 0 { return 0 } - for env, envVar := range cmd.envVars { + for env, envVar := range cmd.env { v := os.Getenv(env) envVar.flags = envVar.flags | IsRequired err := envVar.validateValue(v) @@ -219,7 +220,7 @@ func (c *CLI) checkEnvVars(cmd *Cmd) int { return 0 } -func (c *CLI) processOnTrue(cmd *Cmd, fs []string, nflags map[string]interface{}, aflags map[string]interface{}) { +func (c *Broccli) processOnTrue(cmd *Command, fs []string, nflags map[string]interface{}, aflags map[string]interface{}) { for _, name := range fs { if cmd.flags[name].valueType != TypeBool { continue @@ -236,7 +237,7 @@ func (c *CLI) processOnTrue(cmd *Cmd, fs []string, nflags map[string]interface{} } } -func (c *CLI) processFlags(cmd *Cmd, fs []string, nflags map[string]interface{}, aflags map[string]interface{}) int { +func (c *Broccli) processFlags(cmd *Command, fs []string, nflags map[string]interface{}, aflags map[string]interface{}) int { for _, name := range fs { flag := cmd.flags[name] @@ -272,7 +273,7 @@ func (c *CLI) processFlags(cmd *Cmd, fs []string, nflags map[string]interface{}, return 0 } -func (c *CLI) processArgs(cmd *Cmd, as []string, args []string) int { +func (c *Broccli) processArgs(cmd *Command, as []string, args []string) int { for i, n := range as { v := "" if len(args) >= i+1 { @@ -292,7 +293,7 @@ func (c *CLI) processArgs(cmd *Cmd, as []string, args []string) int { return 0 } -func (c *CLI) processOnPostValidation(cmd *Cmd) int { +func (c *Broccli) processOnPostValidation(cmd *Command) int { if cmd.options.onPostValidation == nil { return 0 } @@ -307,9 +308,9 @@ func (c *CLI) processOnPostValidation(cmd *Cmd) int { return 0 } -func (c *CLI) parseFlags(cmd *Cmd) int { +func (c *Broccli) parseFlags(cmd *Command) int { // check required environment variables - if exitCode := c.checkEnvVars(cmd); exitCode != 0 { + if exitCode := c.checkEnv(cmd); exitCode != 0 { return exitCode } @@ -337,7 +338,7 @@ func (c *CLI) parseFlags(cmd *Cmd) int { return 0 } -func (c *CLI) getParamTypeName(t int8) string { +func (c *Broccli) getParamTypeName(t int8) string { if t == ParamArg { return "Argument" } diff --git a/cli_test.go b/cli_test.go index 092530b..24465c0 100644 --- a/cli_test.go +++ b/cli_test.go @@ -1,6 +1,7 @@ package broccli import ( + "context" "fmt" "io" "log" @@ -25,54 +26,54 @@ func TestCLI(t *testing.T) { os.Stdout = devNull os.Stderr = devNull - c := NewCLI("Example", "App", "Author ") - cmd1 := c.AddCmd("cmd1", "Prints out a string", func(c *CLI) int { + c := NewBroccli("Example", "App", "Author ") + cmd1 := c.Command("cmd1", "Prints out a string", func(ctx context.Context, c *Broccli) int { fmt.Fprintf(f, "TESTVALUE:%s%s\n\n", c.Flag("tekst"), c.Flag("alphanumdots")) if c.Flag("bool") == "true" { fmt.Fprintf(f, "BOOL:true") } return 2 }) - cmd1.AddFlag("tekst", "t", "Text", "Text to print", TypeString, IsRequired) - cmd1.AddFlag("alphanumdots", "a", "Alphanum with dots", "Can have dots", TypeAlphanumeric, AllowDots) - cmd1.AddFlag("make-required", "r", "", "Make alphanumdots required", TypeBool, 0, OnTrue(func(c *Cmd) { + cmd1.Flag("tekst", "t", "Text", "Text to print", TypeString, IsRequired) + cmd1.Flag("alphanumdots", "a", "Alphanum with dots", "Can have dots", TypeAlphanumeric, AllowDots) + cmd1.Flag("make-required", "r", "", "Make alphanumdots required", TypeBool, 0, OnTrue(func(c *Command) { c.flags["alphanumdots"].flags = c.flags["alphanumdots"].flags | IsRequired })) // Boolean should work fine even when the optional OnTrue is not passed - cmd1.AddFlag("bool", "b", "", "Bool value", TypeBool, 0) + cmd1.Flag("bool", "b", "", "Bool value", TypeBool, 0) os.Args = []string{"test", "cmd1"} - got := c.Run() + got := c.Run(context.Background()) if got != 1 { t.Errorf("CLI.Run() should have returned 1 instead of %d", got) } os.Args = []string{"test", "cmd1", "-t", ""} - got = c.Run() + got = c.Run(context.Background()) if got != 1 { t.Errorf("CLI.Run() should have returned 1 instead of %d", got) } os.Args = []string{"test", "cmd1", "--tekst", "Tekst123", "--alphanumdots"} - got = c.Run() + got = c.Run(context.Background()) if got != 2 { t.Errorf("CLI.Run() should have returned 2 instead of %d", got) } os.Args = []string{"test", "cmd1", "--tekst", "Tekst123", "-r"} - got = c.Run() + got = c.Run(context.Background()) if got != 1 { t.Errorf("CLI.Run() should have returned 1 instead of %d", got) } os.Args = []string{"test", "cmd1", "--tekst", "Tekst123", "--alphanumdots", "aZ0-9"} - got = c.Run() + got = c.Run(context.Background()) if got != 1 { t.Errorf("CLI.Run() should have returned 1 instead of %d", got) } os.Args = []string{"test", "cmd1", "--tekst", "Tekst123", "--alphanumdots", "aZ0.9", "-b"} - got = c.Run() + got = c.Run(context.Background()) if got != 2 { t.Errorf("CLI.Run() should have returned 2 instead of %d", got) } diff --git a/cmd.go b/cmd.go index 1857be3..10130bf 100644 --- a/cmd.go +++ b/cmd.go @@ -1,6 +1,7 @@ package broccli import ( + "context" "fmt" "log" "os" @@ -10,32 +11,32 @@ import ( "text/tabwriter" ) -// Cmd represent a command which has a name (used in args when calling app), description, a handler that is called. +// Command represent a command which has a name (used in args when calling app), usage, a handler that is called. // Such command can have flags and arguments. In addition to that, required environment variables can be set. -type Cmd struct { +type Command struct { name string - desc string + usage string flags map[string]*param args map[string]*param argsOrder []string argsIdx int - envVars map[string]*param - handler func(*CLI) int - options cmdOptions + env map[string]*param + handler func(context.Context, *Broccli) int + options commandOptions } -// AddFlag adds a flag to a command and returns a pointer to Param instance. +// Flag adds a flag to a command and returns a pointer to Param instance. // Method requires name (eg. 'data' for '--data', alias (eg. 'd' for '-d'), placeholder for the value displayed on the -// 'help' screen, description, type of the value and additional validation that is set up with bit flags, eg. IsRequired +// 'help' screen, usage, type of the value and additional validation that is set up with bit flags, eg. IsRequired // or AllowMultipleValues. If no additional flags are required, 0 should be used. -func (c *Cmd) AddFlag(name string, alias string, valuePlaceholder string, description string, types int64, flags int64, opts ...paramOption) { +func (c *Command) Flag(name, alias, valuePlaceholder, usage string, types, flags int64, opts ...paramOption) { if c.flags == nil { c.flags = map[string]*param{} } c.flags[name] = ¶m{ name: name, alias: alias, - desc: description, + usage: usage, valuePlaceholder: valuePlaceholder, valueType: types, flags: flags, @@ -46,9 +47,9 @@ func (c *Cmd) AddFlag(name string, alias string, valuePlaceholder string, descri } } -// AddArg adds an argument to a command and returns a pointer to Param instance. It is the same as adding flag except +// Arg adds an argument to a command and returns a pointer to Param instance. It is the same as adding flag except // it does not have an alias. -func (c *Cmd) AddArg(name string, valuePlaceholder string, description string, types int64, flags int64, opts ...paramOption) { +func (c *Command) Arg(name, valuePlaceholder, usage string, types, flags int64, opts ...paramOption) { if c.argsIdx > 9 { log.Fatal("Only 10 arguments are allowed") } @@ -57,7 +58,7 @@ func (c *Cmd) AddArg(name string, valuePlaceholder string, description string, t } c.args[name] = ¶m{ name: name, - desc: description, + usage: usage, valuePlaceholder: valuePlaceholder, valueType: types, flags: flags, @@ -73,22 +74,22 @@ func (c *Cmd) AddArg(name string, valuePlaceholder string, description string, t } } -// AddEnvVar adds a required environment variable to a command and returns a pointer to Param. It's arguments are very +// Env adds a required environment variable to a command and returns a pointer to Param. It's arguments are very // similar to ones in previous AddArg and AddFlag methods. -func (c *Cmd) AddEnvVar(name string, description string, types int64, flags int64, opts ...paramOption) { - if c.envVars == nil { - c.envVars = map[string]*param{} +func (c *Command) Env(name, usage string, types, flags int64, opts ...paramOption) { + if c.env == nil { + c.env = map[string]*param{} } - c.envVars[name] = ¶m{ + c.env[name] = ¶m{ name: name, - desc: description, + usage: usage, valueType: types, flags: flags, options: paramOptions{}, } } -func (c *Cmd) sortedArgs() []string { +func (c *Command) sortedArgs() []string { args := make([]string, c.argsIdx) idx := 0 for i := 0; i < c.argsIdx; i++ { @@ -110,7 +111,7 @@ func (c *Cmd) sortedArgs() []string { return args } -func (c *Cmd) sortedFlags() []string { +func (c *Command) sortedFlags() []string { fs := reflect.ValueOf(c.flags).MapKeys() sfs := make([]string, len(fs)) for i, f := range fs { @@ -120,8 +121,8 @@ func (c *Cmd) sortedFlags() []string { return sfs } -func (c *Cmd) sortedEnvVars() []string { - evs := reflect.ValueOf(c.envVars).MapKeys() +func (c *Command) sortedEnv() []string { + evs := reflect.ValueOf(c.env).MapKeys() sevs := make([]string, len(evs)) for i, ev := range evs { sevs[i] = ev.String() @@ -131,17 +132,17 @@ func (c *Cmd) sortedEnvVars() []string { } // PrintHelp prints command usage information to stdout file. -func (c *Cmd) printHelp() { +func (c *Command) printHelp() { fmt.Fprintf(os.Stdout, "\nUsage: %s %s [FLAGS]%s\n\n", path.Base(os.Args[0]), c.name, c.argsHelpLine()) - fmt.Fprintf(os.Stdout, "%s\n", c.desc) + fmt.Fprintf(os.Stdout, "%s\n", c.usage) - if len(c.envVars) > 0 { + if len(c.env) > 0 { fmt.Fprintf(os.Stdout, "\nRequired environment variables:\n") w := new(tabwriter.Writer) w.Init(os.Stdout, 8, 8, 0, '\t', 0) - for _, n := range c.sortedEnvVars() { - fmt.Fprintf(w, "%s\t%s\n", n, c.envVars[n].desc) + for _, n := range c.sortedEnv() { + fmt.Fprintf(w, "%s\t%s\n", n, c.env[n].usage) } w.Flush() } @@ -174,7 +175,7 @@ func (c *Cmd) printHelp() { } -func (c *Cmd) argsHelpLine() string { +func (c *Command) argsHelpLine() string { sr := "" so := "" if c.argsIdx > 0 { diff --git a/cmd_options.go b/cmd_options.go index e10c2f1..d5e18c4 100644 --- a/cmd_options.go +++ b/cmd_options.go @@ -1,13 +1,13 @@ package broccli -type cmdOptions struct { - onPostValidation func(c *Cmd) error +type commandOptions struct { + onPostValidation func(c *Command) error } -type cmdOption func(opts *cmdOptions) +type commandOption func(opts *commandOptions) -func OnPostValidation(fn func(c *Cmd) error) cmdOption { - return func(opts *cmdOptions) { +func OnPostValidation(fn func(c *Command) error) commandOption { + return func(opts *commandOptions) { opts.onPostValidation = fn } } diff --git a/cmd_test.go b/cmd_test.go index 56972fe..de4dbc5 100644 --- a/cmd_test.go +++ b/cmd_test.go @@ -5,21 +5,21 @@ import ( "testing" ) -// TestCmdParams creates a dummy Cmd instance and tests attaching flags, args and environment variables. -func TestCmdParams(t *testing.T) { - c := &Cmd{} - c.AddFlag("flag1", "f1", "int", "Flag 1", TypeInt, IsRequired) - c.AddFlag("flag2", "f2", "path", "Flag 2", TypePathFile, IsRegularFile) - c.AddFlag("flag3", "f3", "", "Flag 3", TypeBool, 0, OnTrue(func(c *Cmd) { +// TestCommandParams creates a dummy Command instance and tests attaching flags, args and environment variables. +func TestCommandParams(t *testing.T) { + c := &Command{} + c.Flag("flag1", "f1", "int", "Flag 1", TypeInt, IsRequired) + c.Flag("flag2", "f2", "path", "Flag 2", TypePathFile, IsRegularFile) + c.Flag("flag3", "f3", "", "Flag 3", TypeBool, 0, OnTrue(func(c *Command) { c.flags["flag2"].flags = c.flags["flag2"].flags | IsExistent })) - c.AddArg("arg1", "ARG1", "Arg 1", TypeInt, IsRequired) - c.AddArg("arg2", "ARG2", "Arg 2", TypeAlphanumeric, 0) - c.AddEnvVar("ENVVAR1", "Env var 1", TypeInt, 0) + c.Arg("arg1", "ARG1", "Arg 1", TypeInt, IsRequired) + c.Arg("arg2", "ARG2", "Arg 2", TypeAlphanumeric, 0) + c.Env("ENVVAR1", "Env var 1", TypeInt, 0) sa := c.sortedArgs() sf := c.sortedFlags() - se := c.sortedEnvVars() + se := c.sortedEnv() if len(sa) != 2 || len(sf) != 3 || len(se) != 1 { t.Errorf("Invalid args or flags or env vars added") diff --git a/go.mod b/go.mod index 8d47e16..34e1e39 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ -module gopkg.pl/phings/broccli/v2 +module gopkg.pl/phings/broccli/v3 go 1.22.1 diff --git a/param.go b/param.go index 9c6735f..9a19dfe 100644 --- a/param.go +++ b/param.go @@ -9,7 +9,7 @@ import ( ) // param represends a value and it is used for flags, args and environment variables. -// It has a name, alias, description, value that is shown when printing help, specific type (eg. TypeBool or TypeInt), +// It has a name, alias, usage, value that is shown when printing help, specific type (eg. TypeBool or TypeInt), // If more than one value shoud be allowed, eg. '1,2,3' means "multiple integers" and the separator here is ','. // Additional characters are used with type of TypeAlphanumeric to allow dots, underscore etc. Hence, the value of that // arg could be '._-'. @@ -17,7 +17,7 @@ type param struct { name string alias string valuePlaceholder string - desc string + usage string valueType int64 flags int64 options paramOptions @@ -31,7 +31,7 @@ func (p *param) helpLine() string { } else { s += fmt.Sprintf(" -%s,\t", p.alias) } - s += fmt.Sprintf(" --%s %s \t%s\n", p.name, p.valuePlaceholder, p.desc) + s += fmt.Sprintf(" --%s %s \t%s\n", p.name, p.valuePlaceholder, p.usage) return s } diff --git a/param_options.go b/param_options.go index 87a94d5..359fc20 100644 --- a/param_options.go +++ b/param_options.go @@ -1,12 +1,12 @@ package broccli type paramOptions struct { - onTrue func(c *Cmd) + onTrue func(c *Command) } type paramOption func(opts *paramOptions) -func OnTrue(fn func(c *Cmd)) paramOption { +func OnTrue(fn func(c *Command)) paramOption { return func(opts *paramOptions) { opts.onTrue = fn } diff --git a/param_test.go b/param_test.go index 0545f50..7a94e51 100644 --- a/param_test.go +++ b/param_test.go @@ -6,7 +6,7 @@ import ( "testing" ) -func h(c *CLI) int { +func h(c *Broccli) int { return 0 } diff --git a/version.go b/version.go index c2fa2f5..8286ce6 100644 --- a/version.go +++ b/version.go @@ -1,3 +1,3 @@ package broccli -const VERSION = "2.2.3" +const VERSION = "3.0.0"