Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom help #50

Merged
merged 6 commits into from
Jan 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 28 additions & 3 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 @@ -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
Expand All @@ -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)
Expand All @@ -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.
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
185 changes: 184 additions & 1 deletion argparse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}
20 changes: 10 additions & 10 deletions argument.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
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
19 changes: 19 additions & 0 deletions examples/help-argument-names/help-argument-names.go
Original file line number Diff line number Diff line change
@@ -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"}))
}
20 changes: 20 additions & 0 deletions examples/help-no-exit/help-no-exit.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 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")
}
20 changes: 20 additions & 0 deletions examples/no-help/no-help.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 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"}))
}