Skip to content

Commit

Permalink
Custom help (#50)
Browse files Browse the repository at this point in the history
* Finishes addressing issue #29

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.

* Finishing Issue #29

Fixed merge conflict error due to check function return type change confusion.

* Updated Settings Object to multiple function calls

Added functions:
	DisableHelp
	SetHelp
	ExitOnHelp

Added Command.exitOnHelp

Added more tests to increase code coverage

* Updated no help example
  • Loading branch information
densestvoid authored Jan 29, 2020
1 parent 48862cb commit 6876ef2
Show file tree
Hide file tree
Showing 8 changed files with 292 additions and 19 deletions.
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"}))
}

0 comments on commit 6876ef2

Please sign in to comment.
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy