summaryrefslogtreecommitdiff
path: root/parse.go
diff options
context:
space:
mode:
Diffstat (limited to 'parse.go')
-rw-r--r--parse.go48
1 files changed, 26 insertions, 22 deletions
diff --git a/parse.go b/parse.go
index 7472d51..e11c336 100644
--- a/parse.go
+++ b/parse.go
@@ -56,7 +56,7 @@ type spec struct {
env string // the name of the environment variable for this option, or empty for none
defaultValue reflect.Value // default value for this option
defaultString string // default value for this option, in string form to be displayed in help text
- placeholder string // name of the data in help
+ placeholder string // placeholder string in help
}
// command represents a named subcommand, or the top-level command
@@ -76,8 +76,9 @@ var ErrHelp = errors.New("help requested by user")
// ErrVersion indicates that the builtin --version was provided
var ErrVersion = errors.New("version requested by user")
-// for monkey patching in example code
+// for monkey patching in example and test code
var mustParseExit = os.Exit
+var mustParseOut io.Writer = os.Stdout
// This stores the args sent from modules
var register []interface{}
@@ -98,23 +99,16 @@ func Register(dest ...interface{}) {
// MustParse processes command line arguments and exits upon failure
func MustParse(dest ...interface{}) *Parser {
register = append(register, dest...)
- return mustParse(Config{Exit: mustParseExit}, register...)
+ return mustParse(Config{Exit: mustParseExit, Out: mustParseOut}, register...)
}
// mustParse is a helper that facilitates testing
func mustParse(config Config, dest ...interface{}) *Parser {
- if config.Exit == nil {
- config.Exit = os.Exit
- }
- if config.Out == nil {
- config.Out = os.Stdout
- }
-
p, err := NewParser(config, dest...)
if err != nil {
fmt.Fprintln(config.Out, err)
- config.Exit(-1)
+ config.Exit(2)
return nil
}
@@ -155,6 +149,9 @@ type Config struct {
// subcommand
StrictSubcommands bool
+ // EnvPrefix instructs the library to use a name prefix when reading environment variables.
+ EnvPrefix string
+
// Exit is called to terminate the process with an error code (defaults to os.Exit)
Exit func(int)
@@ -259,7 +256,7 @@ func NewParser(config Config, dests ...interface{}) (*Parser, error) {
panic(fmt.Sprintf("%s is not a pointer (did you forget an ampersand?)", t))
}
- cmd, err := cmdFromStruct(name, path{root: i}, t)
+ cmd, err := cmdFromStruct(name, path{root: i}, t, config.EnvPrefix)
if err != nil {
return nil, err
}
@@ -309,7 +306,7 @@ func NewParser(config Config, dests ...interface{}) (*Parser, error) {
return &p, nil
}
-func cmdFromStruct(name string, dest path, t reflect.Type) (*command, error) {
+func cmdFromStruct(name string, dest path, t reflect.Type, envPrefix string) (*command, error) {
// commands can only be created from pointers to structs
if t.Kind() != reflect.Ptr {
return nil, fmt.Errorf("subcommands must be pointers to structs but %s is a %s",
@@ -360,9 +357,8 @@ func cmdFromStruct(name string, dest path, t reflect.Type) (*command, error) {
spec.help = help
}
- // Look at the tag
- var isSubcommand bool // tracks whether this field is a subcommand
-
+ // process each comma-separated part of the tag
+ var isSubcommand bool
for _, key := range strings.Split(tag, ",") {
if key == "" {
continue
@@ -397,9 +393,9 @@ func cmdFromStruct(name string, dest path, t reflect.Type) (*command, error) {
case key == "env":
// Use override name if provided
if value != "" {
- spec.env = value
+ spec.env = envPrefix + value
} else {
- spec.env = strings.ToUpper(field.Name)
+ spec.env = envPrefix + strings.ToUpper(field.Name)
}
case key == "subcommand":
// decide on a name for the subcommand
@@ -414,7 +410,7 @@ func cmdFromStruct(name string, dest path, t reflect.Type) (*command, error) {
}
// parse the subcommand recursively
- subcmd, err := cmdFromStruct(cmdnames[0], subdest, field.Type)
+ subcmd, err := cmdFromStruct(cmdnames[0], subdest, field.Type, envPrefix)
if err != nil {
errs = append(errs, err.Error())
return false
@@ -432,6 +428,7 @@ func cmdFromStruct(name string, dest path, t reflect.Type) (*command, error) {
}
}
+ // placeholder is the string used in the help text like this: "--somearg PLACEHOLDER"
placeholder, hasPlaceholder := field.Tag.Lookup("placeholder")
if hasPlaceholder {
spec.placeholder = placeholder
@@ -517,8 +514,15 @@ func cmdFromStruct(name string, dest path, t reflect.Type) (*command, error) {
return &cmd, nil
}
-// Parse processes the given command line option, storing the results in the field
-// of the structs from which NewParser was constructed
+// Parse processes the given command line option, storing the results in the fields
+// of the structs from which NewParser was constructed.
+//
+// It returns ErrHelp if "--help" is one of the command line args and ErrVersion if
+// "--version" is one of the command line args (the latter only applies if the
+// destination struct passed to NewParser implements Versioned.)
+//
+// To respond to --help and --version in the way that MustParse does, see examples
+// in the README under "Custom handling of --help and --version".
func (p *Parser) Parse(args []string) error {
err := p.process(args)
if err != nil {
@@ -632,7 +636,7 @@ func (p *Parser) process(args []string) error {
// must use explicit for loop, not range, because we manipulate i inside the loop
for i := 0; i < len(args); i++ {
arg := args[i]
- if arg == "--" {
+ if arg == "--" && !allpositional {
allpositional = true
continue
}