Skip to content

Commit

Permalink
Finishes addressing issue akamensky#29
Browse files Browse the repository at this point in the history
Adds a Settings struct for creating parsers/commands with
NewParserWithSettings or NewCommandWithSettings.

Settings
	HelpDisabled -	defaults false, set true to not generate a help
			argument for parser/command
	HelpSname -	short name for the parser/command help argument.
			Can be left empty
	HelpLname -	long name for the parser/command help argument.
			Leaving empty forces use of -h/--help when
			HelpDisabled is false
	NoExitOnHelp -	defaults false, set true to not exit on help
			argument being parsed

Should resolve all outstanding help argument issues.
  • Loading branch information
densestvoid committed Oct 18, 2019
1 parent 1427fe6 commit 0f6d107
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 15 deletions.
75 changes: 73 additions & 2 deletions argparse.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -97,6 +98,25 @@ type Options struct {
Default interface{}
}

// Settings are specific settings for a parser or command. They can be provided if necessary.
// Possible fields are:
//
// Settings.HelpDisabled - if true, does not creates a help argument for the Parser/Command.
// useful when a help argument should not be created.
//
// Settings.HelpSname - A string which will be used as the help argument short name. Can be left empty
//
// Settings.HelpLname - A string which will be used as the help argument long name. If left empty,
// -h and --help are used as the short and long names repectively
//
//Settings.NoExitOnHelp - if true, does call os.Exit when help arguments are parsed
type Settings struct {
HelpDisabled bool
HelpSname string
HelpLname string
NoExitOnHelp bool
}

// NewParser creates new Parser object that will allow to add arguments for parsing
// It takes program name and description which will be used as part of Usage output
// Returns pointer to Parser object
Expand All @@ -109,7 +129,29 @@ 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
}

// NewParserWithSettings creates new Parser object that will allow to add arguments for parsing
// It takes program name and description which will be used as part of Usage output
// Returns pointer to Parser object
func NewParserWithSettings(name string, description string, settings Settings) *Parser {
p := &Parser{}

p.name = name
p.description = description

p.args = make([]*arg, 0)
p.commands = make([]*Command, 0)

if !settings.HelpDisabled {
p.help(settings.HelpSname, settings.HelpLname)
}
p.exitOnHelp = !settings.NoExitOnHelp
p.HelpFunc = (*Command).Usage

return p
Expand All @@ -129,7 +171,36 @@ func (o *Command) NewCommand(name string, description string) *Command {
c.parsed = false
c.parent = o

c.help()
c.help("h", "help")
c.exitOnHelp = true

if o.commands == nil {
o.commands = make([]*Command, 0)
}

o.commands = append(o.commands, c)

return c
}

// NewCommandWithSettings will create a sub-command and propagate all necessary fields.
// All commands are always at the beginning of the arguments.
// Parser can have commands and those commands can have sub-commands,
// which allows for very flexible workflow.
// All commands are considered as required and all commands can have their own argument set.
// Commands are processed Parser -> Command -> sub-Command.
// Arguments will be processed in order of sub-Command -> Command -> Parser.
func (o *Command) NewCommandWithSettings(name string, description string, settings Settings) *Command {
c := new(Command)
c.name = name
c.description = description
c.parsed = false
c.parent = o

if !settings.HelpDisabled {
c.help(settings.HelpSname, settings.HelpLname)
}
c.exitOnHelp = !settings.NoExitOnHelp

if o.commands == nil {
o.commands = make([]*Command, 0)
Expand Down
4 changes: 3 additions & 1 deletion argparse_examples_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package argparse

import "fmt"
import (
"fmt"
)

func ExampleCommand_Help() {
parser := NewParser("parser", "")
Expand Down
28 changes: 28 additions & 0 deletions argparse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1186,3 +1186,31 @@ func TestNewParserHelpFuncDefault(t *testing.T) {
t.Errorf("HelpFunc should default to Usage function")
}
}

func TestNewParserWithSettingsHelpDisabled(t *testing.T) {
parser := NewParserWithSettings("parser", "", Settings{HelpDisabled: true})
if len(parser.args) > 0 {
t.Errorf("Parser should not have any arguments")
}
if err := parser.Parse([]string{"parser", "-h"}); err == nil {
t.Errorf("Parsing should fail, help argument shouldn't exist")
}
}

func TestNewParserWithSettingsHelpNames(t *testing.T) {
sname, lname := "x", "xyz"
parser := NewParserWithSettings("parser", "", Settings{HelpSname: sname, HelpLname: lname})
if len(parser.args) != 1 {
t.Errorf("Parser should not have any arguments:\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)
}
}
22 changes: 14 additions & 8 deletions argument.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,17 @@ func (o arg) GetLname() string {

type help struct{}

func (o *arg) check(argument string) bool {
// Shortcut to showing help
if argument == "-h" || argument == "--help" {
helpText := o.parent.Help(nil)
fmt.Print(helpText)
os.Exit(0)
}
// HelpCalled error returned when parsing a help argument
// instead of exiting to prevent further parsing
type HelpCalled struct {
arg arg
}

func (hc *HelpCalled) Error() string {
return fmt.Sprintf("Command %s help argument parsed", hc.arg.parent.name)
}

func (o *arg) check(argument string) bool {
// Check for long name only if not empty
if o.lname != "" {
// If argument begins with "--" and next is not "-" then it is a long name
Expand Down Expand Up @@ -137,7 +140,10 @@ func (o *arg) parse(args []string) error {
case *help:
helpText := o.parent.Help(nil)
fmt.Print(helpText)
os.Exit(0)
if o.parent.exitOnHelp {
os.Exit(0)
}
return &HelpCalled{*o}
case *bool:
*o.result.(*bool) = true
o.parsed = true
Expand Down
12 changes: 8 additions & 4 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
20 changes: 20 additions & 0 deletions examples/help-argument-names/help-argument-names.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package main

import (
"fmt"

"github.com/akamensky/argparse"
)

func main() {
// Create Parser Settings
settings := argparse.Settings{HelpSname: "e", HelpLname: "example"}
// Create new parser object
parser := argparse.NewParserWithSettings("help", "Demonstrates changing the help argument names", settings)
// 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"}))
}
21 changes: 21 additions & 0 deletions examples/help-no-exit/help-no-exit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package main

import (
"fmt"

"github.com/akamensky/argparse"
)

func main() {
// Create Parser Settings
settings := argparse.Settings{NoExitOnHelp: true}
// Create new parser object
parser := argparse.NewParserWithSettings("help", "Demonstrates changing the help argument names", settings)
// 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")
}
21 changes: 21 additions & 0 deletions examples/no-help/no-help.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package main

import (
"fmt"

"github.com/akamensky/argparse"
)

func main() {
// Create Parser Settings
settings := argparse.Settings{HelpDisabled: true}
// Create new parser object
parser := argparse.NewParserWithSettings("help", "Demonstrates disabing the help arguments", settings)
// 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"}))
}

0 comments on commit 0f6d107

Please sign in to comment.