diff --git a/parse.go b/parse.go index 6768699..c959656 100644 --- a/parse.go +++ b/parse.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "os" + "path/filepath" "reflect" "strings" ) @@ -28,7 +29,7 @@ var ErrHelp = errors.New("help requested by user") // MustParse processes command line arguments and exits upon failure func MustParse(dest ...interface{}) *Parser { - p, err := NewParser(dest...) + p, err := NewParser(Config{}, dest...) if err != nil { fmt.Println(err) os.Exit(-1) @@ -46,20 +47,26 @@ func MustParse(dest ...interface{}) *Parser { // Parse processes command line arguments and stores them in dest func Parse(dest ...interface{}) error { - p, err := NewParser(dest...) + p, err := NewParser(Config{}, dest...) if err != nil { return err } return p.Parse(os.Args[1:]) } +// Config represents configuration options for an argument parser +type Config struct { + Program string // Program is the name of the program used in the help text +} + // Parser represents a set of command line options with destination values type Parser struct { - spec []*spec + spec []*spec + config Config } // NewParser constructs a parser from a list of destination structs -func NewParser(dests ...interface{}) (*Parser, error) { +func NewParser(config Config, dests ...interface{}) (*Parser, error) { var specs []*spec for _, dest := range dests { v := reflect.ValueOf(dest) @@ -134,7 +141,16 @@ func NewParser(dests ...interface{}) (*Parser, error) { specs = append(specs, &spec) } } - return &Parser{spec: specs}, nil + if config.Program == "" { + config.Program = "program" + if len(os.Args) > 0 { + config.Program = filepath.Base(os.Args[0]) + } + } + return &Parser{ + spec: specs, + config: config, + }, nil } // Parse processes the given command line option, storing the results in the field diff --git a/parse_test.go b/parse_test.go index e33fe76..964c9a7 100644 --- a/parse_test.go +++ b/parse_test.go @@ -13,7 +13,7 @@ import ( ) func parse(cmdline string, dest interface{}) error { - p, err := NewParser(dest) + p, err := NewParser(Config{}, dest) if err != nil { return err } diff --git a/usage.go b/usage.go index eea6d29..9e9ce77 100644 --- a/usage.go +++ b/usage.go @@ -4,7 +4,6 @@ import ( "fmt" "io" "os" - "path/filepath" "reflect" "strings" ) @@ -30,7 +29,7 @@ func (p *Parser) WriteUsage(w io.Writer) { } } - fmt.Fprintf(w, "usage: %s", filepath.Base(os.Args[0])) + fmt.Fprintf(w, "usage: %s", p.config.Program) // write the option component of the usage message for _, spec := range options { @@ -114,8 +113,7 @@ func printOption(w io.Writer, spec *spec) { } fmt.Fprint(w, spec.help) } - // Check if spec.dest is zero value or not - // If it isn't a default value have been added + // If spec.dest is not the zero value then a default value has been added. v := spec.dest if v.IsValid() { z := reflect.Zero(v.Type()) diff --git a/usage_test.go b/usage_test.go index 255018d..fd2ba3a 100644 --- a/usage_test.go +++ b/usage_test.go @@ -43,7 +43,7 @@ options: } args.Name = "Foo Bar" args.Value = 42 - p, err := NewParser(&args) + p, err := NewParser(Config{}, &args) require.NoError(t, err) os.Args[0] = "example" @@ -71,7 +71,25 @@ options: VeryLongPositionalWithHelp string `arg:"positional,help:this positional argument is very long"` } - p, err := NewParser(&args) + p, err := NewParser(Config{}, &args) + require.NoError(t, err) + + os.Args[0] = "example" + var help bytes.Buffer + p.WriteHelp(&help) + assert.Equal(t, expectedHelp, help.String()) +} + +func TestUsageWithProgramName(t *testing.T) { + expectedHelp := `usage: myprogram + +options: + --help, -h display this help and exit +` + config := Config{ + Program: "myprogram", + } + p, err := NewParser(config, &struct{}{}) require.NoError(t, err) os.Args[0] = "example"