Store default literal value before parsing
Fixes #143.
diff --git a/command_private.go b/command_private.go
index 82ce793..3ae7247 100644
--- a/command_private.go
+++ b/command_private.go
@@ -104,6 +104,16 @@
return c.scanType(c.scanSubcommandHandler(c.Group))
}
+func (c *Command) eachOption(f func(*Command, *Group, *Option)) {
+ c.eachCommand(func(c *Command) {
+ c.eachGroup(func(g *Group) {
+ for _, option := range g.options {
+ f(c, g, option)
+ }
+ })
+ }, true)
+}
+
func (c *Command) eachCommand(f func(*Command), recurse bool) {
f(c)
diff --git a/help.go b/help.go
index 24a5778..5abe257 100644
--- a/help.go
+++ b/help.go
@@ -9,7 +9,6 @@
"bytes"
"fmt"
"io"
- "reflect"
"runtime"
"strings"
"unicode/utf8"
@@ -155,39 +154,12 @@
dw := descstart - written
writer.WriteString(strings.Repeat(" ", dw))
- def := ""
- defs := option.Default
+ var def string
- if len(option.DefaultMask) != 0 {
- if option.DefaultMask != "-" {
- def = option.DefaultMask
- }
- } else if len(defs) == 0 && option.canArgument() {
- var showdef bool
-
- switch option.field.Type.Kind() {
- case reflect.Func, reflect.Ptr:
- showdef = !option.value.IsNil()
- case reflect.Slice, reflect.String, reflect.Array:
- showdef = option.value.Len() > 0
- case reflect.Map:
- showdef = !option.value.IsNil() && option.value.Len() > 0
- default:
- zeroval := reflect.Zero(option.field.Type)
- showdef = !reflect.DeepEqual(zeroval.Interface(), option.value.Interface())
- }
-
- if showdef {
- def, _ = convertToString(option.value, option.tag)
- }
- } else if len(defs) != 0 {
- l := len(defs) - 1
-
- for i := 0; i < l; i++ {
- def += quoteIfNeeded(defs[i]) + ", "
- }
-
- def += quoteIfNeeded(defs[l])
+ if len(option.DefaultMask) != 0 && option.DefaultMask != "-" {
+ def = option.DefaultMask
+ } else {
+ def = option.defaultLiteral
}
var envDef string
diff --git a/help_test.go b/help_test.go
index 1843cba..5dbffb6 100644
--- a/help_test.go
+++ b/help_test.go
@@ -315,3 +315,79 @@
assertDiff(t, e.Message, expected, "help message")
}
}
+
+func TestHelpDefaults(t *testing.T) {
+ var expected string
+
+ if runtime.GOOS == "windows" {
+ expected = `Usage:
+ TestHelpDefaults [OPTIONS]
+
+Application Options:
+ /with-default: With default (default: default-value)
+ /without-default: Without default
+ /with-programmatic-default: With programmatic default (default:
+ default-value)
+
+Help Options:
+ /? Show this help message
+ /h, /help Show this help message
+`
+ } else {
+ expected = `Usage:
+ TestHelpDefaults [OPTIONS]
+
+Application Options:
+ --with-default= With default (default: default-value)
+ --without-default= Without default
+ --with-programmatic-default= With programmatic default (default:
+ default-value)
+
+Help Options:
+ -h, --help Show this help message
+`
+ }
+
+ tests := []struct {
+ Args []string
+ Output string
+ }{
+ {
+ Args: []string{"-h"},
+ Output: expected,
+ },
+ {
+ Args: []string{"--with-default", "other-value", "--with-programmatic-default", "other-value", "-h"},
+ Output: expected,
+ },
+ }
+
+ for _, test := range tests {
+ var opts struct {
+ WithDefault string `long:"with-default" default:"default-value" description:"With default"`
+ WithoutDefault string `long:"without-default" description:"Without default"`
+ WithProgrammaticDefault string `long:"with-programmatic-default" description:"With programmatic default"`
+ }
+
+ opts.WithProgrammaticDefault = "default-value"
+
+ p := NewNamedParser("TestHelpDefaults", HelpFlag)
+ p.AddGroup("Application Options", "The application options", &opts)
+
+ _, err := p.ParseArgs(test.Args)
+
+ if err == nil {
+ t.Fatalf("Expected help error")
+ }
+
+ if e, ok := err.(*Error); !ok {
+ t.Fatalf("Expected flags.Error, but got %T", err)
+ } else {
+ if e.Type != ErrHelp {
+ t.Errorf("Expected flags.ErrHelp type, but got %s", e.Type)
+ }
+
+ assertDiff(t, e.Message, test.Output, "help message")
+ }
+ }
+}
diff --git a/option.go b/option.go
index 73c24f5..068ea32 100644
--- a/option.go
+++ b/option.go
@@ -76,6 +76,8 @@
tag multiTag
isSet bool
+
+ defaultLiteral string
}
// LongNameWithNamespace returns the option's long name with the group namespaces
diff --git a/option_private.go b/option_private.go
index 06fc206..ab63ba2 100644
--- a/option_private.go
+++ b/option_private.go
@@ -203,3 +203,38 @@
return nil
}
+
+func (option *Option) updateDefaultLiteral() {
+ defs := option.Default
+ def := ""
+
+ if len(defs) == 0 && option.canArgument() {
+ var showdef bool
+
+ switch option.field.Type.Kind() {
+ case reflect.Func, reflect.Ptr:
+ showdef = !option.value.IsNil()
+ case reflect.Slice, reflect.String, reflect.Array:
+ showdef = option.value.Len() > 0
+ case reflect.Map:
+ showdef = !option.value.IsNil() && option.value.Len() > 0
+ default:
+ zeroval := reflect.Zero(option.field.Type)
+ showdef = !reflect.DeepEqual(zeroval.Interface(), option.value.Interface())
+ }
+
+ if showdef {
+ def, _ = convertToString(option.value, option.tag)
+ }
+ } else if len(defs) != 0 {
+ l := len(defs) - 1
+
+ for i := 0; i < l; i++ {
+ def += quoteIfNeeded(defs[i]) + ", "
+ }
+
+ def += quoteIfNeeded(defs[l])
+ }
+
+ option.defaultLiteral = def
+}
diff --git a/parser.go b/parser.go
index d403a40..928f225 100644
--- a/parser.go
+++ b/parser.go
@@ -170,7 +170,10 @@
return nil, p.internalError
}
- p.clearIsSet()
+ p.eachOption(func(c *Command, g *Group, option *Option) {
+ option.isSet = false
+ option.updateDefaultLiteral()
+ })
// Add built-in help group to all commands if necessary
if (p.Options & HelpFlag) != None {
@@ -254,17 +257,13 @@
}
if s.err == nil {
- p.eachCommand(func(c *Command) {
- c.eachGroup(func(g *Group) {
- for _, option := range g.options {
- if option.isSet {
- continue
- }
+ p.eachOption(func(c *Command, g *Group, option *Option) {
+ if option.isSet {
+ return
+ }
- option.clearDefault()
- }
- })
- }, true)
+ option.clearDefault()
+ })
s.checkRequired(p)
}