diff --git a/argparse.go b/argparse.go index 9dfc7e8..a743ade 100644 --- a/argparse.go +++ b/argparse.go @@ -23,6 +23,7 @@ type Command struct { happened bool parent *Command HelpFunc func(c *Command, msg interface{}) string + exitOnHelp bool } // GetName exposes Command's name field @@ -109,7 +110,8 @@ func NewParser(name string, description string) *Parser { p.args = make([]*arg, 0) p.commands = make([]*Command, 0) - p.help() + p.help("h", "help") + p.exitOnHelp = true p.HelpFunc = (*Command).Usage return p @@ -128,8 +130,7 @@ func (o *Command) NewCommand(name string, description string) *Command { c.description = description c.parsed = false c.parent = o - - c.help() + c.exitOnHelp = o.exitOnHelp if o.commands == nil { o.commands = make([]*Command, 0) @@ -140,6 +141,30 @@ func (o *Command) NewCommand(name string, description string) *Command { return c } +// DisableHelp removes any help arguments from the commands list of arguments +// This prevents prevents help from being parsed or invoked from the argument list +func (o *Parser) DisableHelp() { + for i, arg := range o.args { + if _, ok := arg.result.(*help); ok { + o.args = append(o.args[:i], o.args[i+1:]...) + } + } +} + +// ExitOnHelp sets the exitOnHelp variable of Parser +func (o *Command) ExitOnHelp(b bool) { + o.exitOnHelp = b + for _, c := range o.commands { + c.ExitOnHelp(b) + } +} + +// SetHelp removes the previous help argument, and creates a new one with the desired sname/lname +func (o *Parser) SetHelp(sname, lname string) { + o.DisableHelp() + o.help(sname, lname) +} + // Flag Creates new flag type of argument, which is boolean value showing if argument was provided or not. // Takes short name, long name and pointer to options (optional). // Short name must be single character, but can be omitted by giving empty string. diff --git a/argparse_examples_test.go b/argparse_examples_test.go index b405ccd..1d38b6f 100644 --- a/argparse_examples_test.go +++ b/argparse_examples_test.go @@ -1,6 +1,8 @@ package argparse -import "fmt" +import ( + "fmt" +) func ExampleCommand_Help() { parser := NewParser("parser", "") diff --git a/argparse_test.go b/argparse_test.go index 3283bc1..cf6497d 100644 --- a/argparse_test.go +++ b/argparse_test.go @@ -2282,9 +2282,192 @@ func TestUsageStringer(t *testing.T) { } } -func TestNewParserHelpFuncDefault(t *testing.T) { +func TestParserHelpFuncDefault(t *testing.T) { parser := NewParser("parser", "") if parser.HelpFunc == nil || parser.Help(nil) != parser.Usage(nil) { t.Errorf("HelpFunc should default to Usage function") } } + +func TestCommandHelpFuncDefault(t *testing.T) { + parser := NewParser("parser", "") + command := parser.NewCommand("command", "") + if command.HelpFunc != nil { + t.Errorf("HelpFunc should default to Usage function") + } +} + +func TestCommandHelpFuncDefaultToParent(t *testing.T) { + parser := NewParser("parser", "") + command := parser.NewCommand("command", "") + + parser.HelpFunc = func(c *Command, msg interface{}) string { + return "testing" + } + + if command.Help(nil) == command.Usage(nil) || command.Help(nil) != parser.Help(nil) { + t.Errorf("command HelpFunc should default to parent function") + } +} + +func TestParserExitOnHelpTrue(t *testing.T) { + exited := false + exit = func(n int) { + exited = true + } + + parser := NewParser("parser", "") + + print = func(...interface{}) (int, error) { + return 0, nil + } + + if err := parser.Parse([]string{"parser", "-h"}); err == nil { + if !exited { + t.Errorf("Parsing help should have invoked os.Exit") + } + } else { + t.Error(err) + } +} + +func TestParserExitOnHelpFalse(t *testing.T) { + exited := false + exit = func(n int) { + exited = true + } + + parser := NewParser("parser", "") + parser.ExitOnHelp(false) + + print = func(...interface{}) (int, error) { + return 0, nil + } + + if err := parser.Parse([]string{"parser", "-h"}); exited { + t.Errorf("Parsing help should not have invoked os.Exit") + } else if err != nil { + t.Error(err) + } +} + +func TestParserDisableHelp(t *testing.T) { + parser := NewParser("parser", "") + parser.DisableHelp() + if len(parser.args) > 0 { + t.Errorf("Parser should not have any arguments") + } + + print = func(...interface{}) (int, error) { + return 0, nil + } + + if err := parser.Parse([]string{"parser", "-h"}); err == nil { + t.Errorf("Parsing should fail, help argument shouldn't exist") + } +} + +func TestParserSetHelp(t *testing.T) { + sname, lname := "x", "xyz" + parser := NewParser("parser", "") + parser.SetHelp(sname, lname) + if len(parser.args) != 1 { + t.Errorf("Parser should have one argument:\n%s", parser.Help(nil)) + } + arg := parser.args[0] + if _, ok := arg.result.(*help); !ok { + t.Errorf("Argument should be %T, is %T", help{}, arg.result) + } + if arg.sname != sname { + t.Errorf("Argument short name should be %s, is %s", sname, arg.sname) + } + if arg.lname != lname { + t.Errorf("Argument long name should be %s, is %s", lname, arg.lname) + } +} + +func TestCommandExitOnHelpTrue(t *testing.T) { + exited := false + exit = func(n int) { + exited = true + } + + parser := NewParser("parser", "") + parser.NewCommand("command", "") + + print = func(...interface{}) (int, error) { + return 0, nil + } + + if err := parser.Parse([]string{"parser", "command", "-h"}); exited { + if err != nil { + t.Error(err) + } + } else { + t.Errorf("Parsing help should have invoked os.Exit") + } +} + +func TestCommandExitOnHelpFalse(t *testing.T) { + exited := false + exit = func(n int) { + exited = true + } + + parser := NewParser("parser", "") + parser.NewCommand("command", "") + parser.ExitOnHelp(false) + + print = func(...interface{}) (int, error) { + return 0, nil + } + + if err := parser.Parse([]string{"parser", "command", "-h"}); exited { + t.Error("Parsing help should not have exited") + } else if err != nil { + t.Error(err) + } +} + +func TestCommandDisableHelp(t *testing.T) { + parser := NewParser("parser", "") + parser.NewCommand("command", "") + parser.DisableHelp() + if len(parser.args) > 0 { + t.Errorf("Parser should not have any arguments") + } + + print = func(...interface{}) (int, error) { + return 0, nil + } + + if err := parser.Parse([]string{"parser", "command", "-h"}); err == nil { + t.Errorf("Parsing should fail, help argument shouldn't exist") + } +} + +func TestCommandHelpInheritance(t *testing.T) { + parser := NewParser("parser", "") + command := parser.NewCommand("command", "") + parser.ExitOnHelp(false) + + if command.exitOnHelp != false { + t.Errorf("Command should inherit exitOnHelp from parent, even after creation") + } +} + +func TestCommandHelpSetSnameOnly(t *testing.T) { + parser := NewParser("parser", "") + parser.SetHelp("q", "") + + arg := parser.args[0] + + _, ok := arg.result.(*help) + if !ok { + t.Error("Argument should be of help type") + } + + if arg.sname != "h" || arg.lname != "help" { + t.Error("Help arugment names should have defaulted") + } +} diff --git a/argument.go b/argument.go index 34ebda9..01bec7c 100644 --- a/argument.go +++ b/argument.go @@ -98,13 +98,6 @@ func (o *arg) checkShortName(argument string) (int, error) { // For shorthand argument - 0 if there is no occurrences, or count of occurrences. // Shorthand argument with parametr, mast be the only or last in the argument string. func (o *arg) check(argument string) (int, error) { - // Shortcut to showing help - if argument == "-h" || argument == "--help" { - helpText := o.parent.Help(nil) - fmt.Print(helpText) - os.Exit(0) - } - rez := o.checkLongName(argument) if rez > 0 { return rez, nil @@ -334,13 +327,20 @@ func (o *arg) parseFileList(args []string) error { return nil } +// To overwrite while testing +// Possibly extend to allow user overriding +var exit func(int) = os.Exit +var print func(...interface{}) (int, error) = fmt.Println + func (o *arg) parseSomeType(args []string, argCount int) error { var err error switch o.result.(type) { case *help: - helpText := o.parent.Help(nil) - fmt.Print(helpText) - os.Exit(0) + print(o.parent.Help(nil)) + if o.parent.exitOnHelp { + exit(0) + } + //data of bool type is for Flag argument case *bool: err = o.parseBool(args) case *int: diff --git a/command.go b/command.go index c6abe3f..da8d7ba 100644 --- a/command.go +++ b/command.go @@ -4,13 +4,17 @@ import ( "fmt" ) -func (o *Command) help() { +func (o *Command) help(sname, lname string) { result := &help{} + if lname == "" { + sname, lname = "h", "help" + } + a := &arg{ - result: &result, - sname: "h", - lname: "help", + result: result, + sname: sname, + lname: lname, size: 1, opts: &Options{Help: "Print help information"}, unique: true, diff --git a/examples/help-argument-names/help-argument-names.go b/examples/help-argument-names/help-argument-names.go new file mode 100644 index 0000000..e8151d0 --- /dev/null +++ b/examples/help-argument-names/help-argument-names.go @@ -0,0 +1,19 @@ +package main + +import ( + "fmt" + + "github.com/akamensky/argparse" +) + +func main() { + // Create new parser object + parser := argparse.NewParser("help", "Demonstrates changing the help argument names") + parser.SetHelp("e", "example") + // Create string flag + parser.String("s", "string", &argparse.Options{Required: false, Help: "String argument example"}) + // Create string flag + parser.Int("i", "int", &argparse.Options{Required: false, Help: "Integer argument example"}) + // Use the help function + fmt.Print(parser.Parse([]string{"parser", "-e"})) +} diff --git a/examples/help-no-exit/help-no-exit.go b/examples/help-no-exit/help-no-exit.go new file mode 100644 index 0000000..a5b5c55 --- /dev/null +++ b/examples/help-no-exit/help-no-exit.go @@ -0,0 +1,20 @@ +package main + +import ( + "fmt" + + "github.com/akamensky/argparse" +) + +func main() { + // Create new parser object + parser := argparse.NewParser("help", "Demonstrates changing the help argument names") + parser.ExitOnHelp(false) + // Create string flag + parser.String("s", "string", &argparse.Options{Required: false, Help: "String argument example"}) + // Create string flag + parser.Int("i", "int", &argparse.Options{Required: false, Help: "Integer argument example"}) + // Use the help function + fmt.Println(parser.Parse([]string{"parser", "-h"})) + fmt.Println("Didn't exit, still printing") +} diff --git a/examples/no-help/no-help.go b/examples/no-help/no-help.go new file mode 100644 index 0000000..422918b --- /dev/null +++ b/examples/no-help/no-help.go @@ -0,0 +1,20 @@ +package main + +import ( + "fmt" + + "github.com/akamensky/argparse" +) + +func main() { + // Create new parser object + parser := argparse.NewParser("help", "Demonstrates disabing the help arguments") + parser.DisableHelp() + // Create string flag + parser.String("s", "string", &argparse.Options{Required: false, Help: "String argument example"}) + // Create string flag + parser.Int("i", "int", &argparse.Options{Required: false, Help: "Integer argument example"}) + + // parsing for -h fails + fmt.Println(parser.Parse([]string{"parser", "-h", "--help", "-s", "testing", "-i", "5"})) +}