Skip to content

Commit

Permalink
Customizable Help Functions
Browse files Browse the repository at this point in the history
Designed to begin addressing issue akamensky#29

HelpFunc added as field for Parsers and Commands
Parser HelpFunc defaults to Usage unless overwritten
Command HelpFunc defaults to first non nil parent HelpFunc

Design choice to enable backwards compatibility required
exposing Command and Arg fields with Getter functions.

Does not resolve:
	exiting program on help invocation
	overwrritting/disabling -h | --help argument strings
	enabling other argument strings to invoke help
  • Loading branch information
densestvoid committed Oct 2, 2019
1 parent e643750 commit 38fb874
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 4 deletions.
38 changes: 36 additions & 2 deletions argparse.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,39 @@ type Command struct {
parsed bool
happened bool
parent *Command
HelpFunc func(c Command, msg interface{}) string
}

func (c Command) GetName() string {
return c.name
}

func (c Command) GetDescription() string {
return c.description
}

func (c Command) GetArgs() []*arg {
return c.args
}

func (c Command) GetCommands() []*Command {
return c.commands
}

func (c Command) GeParent() *Command {
return c.parent
}

// TODO: implement overridden help functions
func (c Command) Help(msg interface{}) string {
tempC := c
for tempC.HelpFunc == nil {
if tempC.parent == nil {
return ""
}
tempC = *tempC.parent
}
return tempC.HelpFunc(c, msg)
}

// Parser is a top level object of argparse. It MUST NOT ever be created manually. Instead one should use
Expand Down Expand Up @@ -67,6 +100,7 @@ func NewParser(name string, description string) *Parser {
p.commands = make([]*Command, 0)

p.help()
p.HelpFunc = Command.Usage

return p
}
Expand Down Expand Up @@ -266,7 +300,7 @@ func (o *Command) Happened() bool {
//
// Accepts an interface that can be error, string or fmt.Stringer that will be prepended to a message.
// All other interface types will be ignored
func (o *Command) Usage(msg interface{}) string {
func (o Command) Usage(msg interface{}) string {
for _, cmd := range o.commands {
if cmd.Happened() {
return cmd.Usage(msg)
Expand All @@ -279,7 +313,7 @@ func (o *Command) Usage(msg interface{}) string {
arguments := make([]*arg, 0)
// First get line of commands until root
var chain []string
current := o
current := &o
if msg != nil {
switch msg.(type) {
case subCommandError:
Expand Down
79 changes: 79 additions & 0 deletions argparse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package argparse

import (
"errors"
"fmt"
"os"
"reflect"
"strconv"
Expand Down Expand Up @@ -1179,3 +1180,81 @@ func TestUsageStringer(t *testing.T) {
t.Errorf("%s", usage)
}
}

func TestNewParserHelpFuncDefault(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 ExampleCommand_Help() {
parser := NewParser("parser", "")
parser.HelpFunc = func(c Command, msg interface{}) string {
return fmt.Sprintf("Name: %s\n", c.GetName())
}
fmt.Println(parser.Help(nil))

// Output:
// Name: parser
}

func ExampleCommand_Help_subcommand_defaulting() {
parser := NewParser("parser", "")
parser.HelpFunc = func(c Command, msg interface{}) string {
helpString := fmt.Sprintf("Name: %s\n", c.GetName())
for _, com := range c.GetCommands() {
// Calls parser.HelpFunc, because command.HelpFuncs are nil
helpString += com.Help(nil)
}
return helpString
}
parser.NewCommand("subcommand1", "")
parser.NewCommand("subcommand2", "")
fmt.Println(parser.Help(nil))

// Output:
// Name: parser
// Name: subcommand1
// Name: subcommand2
}
func ExampleCommand_Help_subcommand_helpfuncs() {
parser := NewParser("parser", "")
parser.HelpFunc = func(c Command, msg interface{}) string {
helpString := fmt.Sprintf("Name: %s\n", c.GetName())
for _, com := range c.GetCommands() {
// Calls command.HelpFunc, because command.HelpFuncs are not nil
helpString += com.Help(nil)
}
return helpString
}
com1 := parser.NewCommand("subcommand1", "Test description")
com1.HelpFunc = func(c Command, msg interface{}) string {
helpString := fmt.Sprintf("Name: %s, Description: %s\n", c.GetName(), c.GetDescription())
return helpString
}
com2 := parser.NewCommand("subcommand2", "")
com2.String("s", "string", &Options{Required: false})
com2.String("i", "integer", &Options{Required: true})
com2.HelpFunc = func(c Command, msg interface{}) string {
helpString := fmt.Sprintf("Name: %s\n", c.GetName())
for _, arg := range c.GetArgs() {
helpString += fmt.Sprintf("\tLname: %s, Required: %t\n", arg.GetLname(), arg.GetOpts().Required)
}
return helpString
}
fmt.Print(parser.Help(nil))
fmt.Print(com1.Help(nil))
fmt.Print(com2.Help(nil))

// Output:
// Name: parser
// Name: subcommand1, Description: Test description
// Name: subcommand2
// Lname: string, Required: false
// Lname: integer, Required: true
// Name: subcommand1, Description: Test description
// Name: subcommand2
// Lname: string, Required: false
// Lname: integer, Required: true
}
16 changes: 14 additions & 2 deletions argument.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,24 @@ type arg struct {
parent *Command // Used to get access to specific Command
}

func (a arg) GetOpts() *Options {
return a.opts
}

func (a arg) GetSname() string {
return a.sname
}

func (a arg) GetLname() string {
return a.lname
}

type help struct{}

func (o *arg) check(argument string) bool {
// Shortcut to showing help
if argument == "-h" || argument == "--help" {
helpText := o.parent.Usage(nil)
helpText := o.parent.Help(nil)
fmt.Print(helpText)
os.Exit(0)
}
Expand Down Expand Up @@ -116,7 +128,7 @@ func (o *arg) parse(args []string) error {

switch o.result.(type) {
case *help:
helpText := o.parent.Usage(nil)
helpText := o.parent.Help(nil)
fmt.Print(helpText)
os.Exit(0)
case *bool:
Expand Down
34 changes: 34 additions & 0 deletions examples/command-help/commands-help.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package main

import (
"fmt"

argparse "../.."
)

func main() {
// Create new parser object
parser := argparse.NewParser("help", "Demonstrates overriding the default help output")
// Replace parser.Usage as the help message
parser.HelpFunc = func(c argparse.Command, msg interface{}) string {
var help string
help += fmt.Sprintf("Name: %s, Description: %s\n", c.GetName(), c.GetDescription())
for _, arg := range c.GetArgs() {
if arg.GetOpts() != nil {
help += fmt.Sprintf("Sname: %s, Lname: %s, Help: %s\n", arg.GetSname(), arg.GetLname(), arg.GetOpts().Help)
} else {
help += fmt.Sprintf("Sname: %s, Lname: %s\n", arg.GetSname(), arg.GetLname())
}
}
return help
}

// Create command
command := parser.NewCommand("sub", "Demonstrates sub command help")
// Create string flag
command.String("s", "string", &argparse.Options{Required: true, Help: "String argument example"})
// Create string flag
command.Int("i", "int", &argparse.Options{Required: true, Help: "Integer argument example"})
// Without declaring command help function, defaults to first non nil parent help function
fmt.Print(command.Help(nil))
}
31 changes: 31 additions & 0 deletions examples/help/help.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package main

import (
"fmt"

argparse "../.."
)

func main() {
// Create new parser object
parser := argparse.NewParser("help", "Demonstrates overriding the default help output")
// Create string flag
parser.String("s", "string", &argparse.Options{Required: true, Help: "String argument example"})
// Create string flag
parser.Int("i", "int", &argparse.Options{Required: true, Help: "Integer argument example"})
// Replace parser.Usage as the help message
parser.HelpFunc = func(c argparse.Command, msg interface{}) string {
var help string
help += fmt.Sprintf("Name: %s, Description: %s\n", c.GetName(), c.GetDescription())
for _, arg := range c.GetArgs() {
if arg.GetOpts() != nil {
help += fmt.Sprintf("Sname: %s, Lname: %s, Help: %s\n", arg.GetSname(), arg.GetLname(), arg.GetOpts().Help)
} else {
help += fmt.Sprintf("Sname: %s, Lname: %s\n", arg.GetSname(), arg.GetLname())
}
}
return help
}
// Use the help function
fmt.Print(parser.Help(nil))
}

0 comments on commit 38fb874

Please sign in to comment.