|
|
@@ -4,24 +4,28 @@ import (
|
|
|
"flag"
|
|
|
"fmt"
|
|
|
"os"
|
|
|
+ "reflect"
|
|
|
"runtime"
|
|
|
"strconv"
|
|
|
"strings"
|
|
|
"time"
|
|
|
)
|
|
|
|
|
|
-// This flag enables bash-completion for all commands and subcommands
|
|
|
+const defaultPlaceholder = "value"
|
|
|
+
|
|
|
+// BashCompletionFlag enables bash-completion for all commands and subcommands
|
|
|
var BashCompletionFlag = BoolFlag{
|
|
|
- Name: "generate-bash-completion",
|
|
|
+ Name: "generate-bash-completion",
|
|
|
+ Hidden: true,
|
|
|
}
|
|
|
|
|
|
-// This flag prints the version for the application
|
|
|
+// VersionFlag prints the version for the application
|
|
|
var VersionFlag = BoolFlag{
|
|
|
Name: "version, v",
|
|
|
Usage: "print the version",
|
|
|
}
|
|
|
|
|
|
-// This flag prints the help for all commands and subcommands
|
|
|
+// HelpFlag prints the help for all commands and subcommands
|
|
|
// Set to the zero value (BoolFlag{}) to disable flag -- keeps subcommand
|
|
|
// unless HideHelp is set to true)
|
|
|
var HelpFlag = BoolFlag{
|
|
|
@@ -29,6 +33,10 @@ var HelpFlag = BoolFlag{
|
|
|
Usage: "show help",
|
|
|
}
|
|
|
|
|
|
+// FlagStringer converts a flag definition to a string. This is used by help
|
|
|
+// to display a flag.
|
|
|
+var FlagStringer FlagStringFunc = stringifyFlag
|
|
|
+
|
|
|
// Flag is a common interface related to parsing flags in cli.
|
|
|
// For more advanced flag parsing techniques, it is recommended that
|
|
|
// this interface be implemented.
|
|
|
@@ -68,24 +76,14 @@ type GenericFlag struct {
|
|
|
Value Generic
|
|
|
Usage string
|
|
|
EnvVar string
|
|
|
+ Hidden bool
|
|
|
}
|
|
|
|
|
|
// String returns the string representation of the generic flag to display the
|
|
|
// help text to the user (uses the String() method of the generic flag to show
|
|
|
// the value)
|
|
|
func (f GenericFlag) String() string {
|
|
|
- return withEnvHint(f.EnvVar, fmt.Sprintf("%s %v\t%v", prefixedNames(f.Name), f.FormatValueHelp(), f.Usage))
|
|
|
-}
|
|
|
-
|
|
|
-func (f GenericFlag) FormatValueHelp() string {
|
|
|
- if f.Value == nil {
|
|
|
- return ""
|
|
|
- }
|
|
|
- s := f.Value.String()
|
|
|
- if len(s) == 0 {
|
|
|
- return ""
|
|
|
- }
|
|
|
- return fmt.Sprintf("\"%s\"", s)
|
|
|
+ return FlagStringer(f)
|
|
|
}
|
|
|
|
|
|
// Apply takes the flagset and calls Set on the generic flag with the value
|
|
|
@@ -107,6 +105,7 @@ func (f GenericFlag) Apply(set *flag.FlagSet) {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
+// GetName returns the name of a flag.
|
|
|
func (f GenericFlag) GetName() string {
|
|
|
return f.Name
|
|
|
}
|
|
|
@@ -130,20 +129,19 @@ func (f *StringSlice) Value() []string {
|
|
|
return *f
|
|
|
}
|
|
|
|
|
|
-// StringSlice is a string flag that can be specified multiple times on the
|
|
|
+// StringSliceFlag is a string flag that can be specified multiple times on the
|
|
|
// command-line
|
|
|
type StringSliceFlag struct {
|
|
|
Name string
|
|
|
Value *StringSlice
|
|
|
Usage string
|
|
|
EnvVar string
|
|
|
+ Hidden bool
|
|
|
}
|
|
|
|
|
|
// String returns the usage
|
|
|
func (f StringSliceFlag) String() string {
|
|
|
- firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ")
|
|
|
- pref := prefixFor(firstName)
|
|
|
- return withEnvHint(f.EnvVar, fmt.Sprintf("%s [%v]\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage))
|
|
|
+ return FlagStringer(f)
|
|
|
}
|
|
|
|
|
|
// Apply populates the flag given the flag set and environment
|
|
|
@@ -171,11 +169,12 @@ func (f StringSliceFlag) Apply(set *flag.FlagSet) {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
+// GetName returns the name of a flag.
|
|
|
func (f StringSliceFlag) GetName() string {
|
|
|
return f.Name
|
|
|
}
|
|
|
|
|
|
-// StringSlice is an opaque type for []int to satisfy flag.Value
|
|
|
+// IntSlice is an opaque type for []int to satisfy flag.Value
|
|
|
type IntSlice []int
|
|
|
|
|
|
// Set parses the value into an integer and appends it to the list of values
|
|
|
@@ -183,9 +182,8 @@ func (f *IntSlice) Set(value string) error {
|
|
|
tmp, err := strconv.Atoi(value)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
- } else {
|
|
|
- *f = append(*f, tmp)
|
|
|
}
|
|
|
+ *f = append(*f, tmp)
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
@@ -206,13 +204,12 @@ type IntSliceFlag struct {
|
|
|
Value *IntSlice
|
|
|
Usage string
|
|
|
EnvVar string
|
|
|
+ Hidden bool
|
|
|
}
|
|
|
|
|
|
// String returns the usage
|
|
|
func (f IntSliceFlag) String() string {
|
|
|
- firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ")
|
|
|
- pref := prefixFor(firstName)
|
|
|
- return withEnvHint(f.EnvVar, fmt.Sprintf("%s [%v]\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage))
|
|
|
+ return FlagStringer(f)
|
|
|
}
|
|
|
|
|
|
// Apply populates the flag given the flag set and environment
|
|
|
@@ -226,7 +223,7 @@ func (f IntSliceFlag) Apply(set *flag.FlagSet) {
|
|
|
s = strings.TrimSpace(s)
|
|
|
err := newVal.Set(s)
|
|
|
if err != nil {
|
|
|
- fmt.Fprintf(os.Stderr, err.Error())
|
|
|
+ fmt.Fprintf(ErrWriter, err.Error())
|
|
|
}
|
|
|
}
|
|
|
f.Value = newVal
|
|
|
@@ -243,6 +240,7 @@ func (f IntSliceFlag) Apply(set *flag.FlagSet) {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
+// GetName returns the name of the flag.
|
|
|
func (f IntSliceFlag) GetName() string {
|
|
|
return f.Name
|
|
|
}
|
|
|
@@ -253,11 +251,12 @@ type BoolFlag struct {
|
|
|
Usage string
|
|
|
EnvVar string
|
|
|
Destination *bool
|
|
|
+ Hidden bool
|
|
|
}
|
|
|
|
|
|
// String returns a readable representation of this value (for usage defaults)
|
|
|
func (f BoolFlag) String() string {
|
|
|
- return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage))
|
|
|
+ return FlagStringer(f)
|
|
|
}
|
|
|
|
|
|
// Apply populates the flag given the flag set and environment
|
|
|
@@ -285,6 +284,7 @@ func (f BoolFlag) Apply(set *flag.FlagSet) {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
+// GetName returns the name of the flag.
|
|
|
func (f BoolFlag) GetName() string {
|
|
|
return f.Name
|
|
|
}
|
|
|
@@ -296,11 +296,12 @@ type BoolTFlag struct {
|
|
|
Usage string
|
|
|
EnvVar string
|
|
|
Destination *bool
|
|
|
+ Hidden bool
|
|
|
}
|
|
|
|
|
|
// String returns a readable representation of this value (for usage defaults)
|
|
|
func (f BoolTFlag) String() string {
|
|
|
- return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage))
|
|
|
+ return FlagStringer(f)
|
|
|
}
|
|
|
|
|
|
// Apply populates the flag given the flag set and environment
|
|
|
@@ -328,6 +329,7 @@ func (f BoolTFlag) Apply(set *flag.FlagSet) {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
+// GetName returns the name of the flag.
|
|
|
func (f BoolTFlag) GetName() string {
|
|
|
return f.Name
|
|
|
}
|
|
|
@@ -339,19 +341,12 @@ type StringFlag struct {
|
|
|
Usage string
|
|
|
EnvVar string
|
|
|
Destination *string
|
|
|
+ Hidden bool
|
|
|
}
|
|
|
|
|
|
// String returns the usage
|
|
|
func (f StringFlag) String() string {
|
|
|
- return withEnvHint(f.EnvVar, fmt.Sprintf("%s %v\t%v", prefixedNames(f.Name), f.FormatValueHelp(), f.Usage))
|
|
|
-}
|
|
|
-
|
|
|
-func (f StringFlag) FormatValueHelp() string {
|
|
|
- s := f.Value
|
|
|
- if len(s) == 0 {
|
|
|
- return ""
|
|
|
- }
|
|
|
- return fmt.Sprintf("\"%s\"", s)
|
|
|
+ return FlagStringer(f)
|
|
|
}
|
|
|
|
|
|
// Apply populates the flag given the flag set and environment
|
|
|
@@ -375,6 +370,7 @@ func (f StringFlag) Apply(set *flag.FlagSet) {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
+// GetName returns the name of the flag.
|
|
|
func (f StringFlag) GetName() string {
|
|
|
return f.Name
|
|
|
}
|
|
|
@@ -387,11 +383,12 @@ type IntFlag struct {
|
|
|
Usage string
|
|
|
EnvVar string
|
|
|
Destination *int
|
|
|
+ Hidden bool
|
|
|
}
|
|
|
|
|
|
// String returns the usage
|
|
|
func (f IntFlag) String() string {
|
|
|
- return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage))
|
|
|
+ return FlagStringer(f)
|
|
|
}
|
|
|
|
|
|
// Apply populates the flag given the flag set and environment
|
|
|
@@ -418,6 +415,7 @@ func (f IntFlag) Apply(set *flag.FlagSet) {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
+// GetName returns the name of the flag.
|
|
|
func (f IntFlag) GetName() string {
|
|
|
return f.Name
|
|
|
}
|
|
|
@@ -430,11 +428,12 @@ type DurationFlag struct {
|
|
|
Usage string
|
|
|
EnvVar string
|
|
|
Destination *time.Duration
|
|
|
+ Hidden bool
|
|
|
}
|
|
|
|
|
|
// String returns a readable representation of this value (for usage defaults)
|
|
|
func (f DurationFlag) String() string {
|
|
|
- return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage))
|
|
|
+ return FlagStringer(f)
|
|
|
}
|
|
|
|
|
|
// Apply populates the flag given the flag set and environment
|
|
|
@@ -461,6 +460,7 @@ func (f DurationFlag) Apply(set *flag.FlagSet) {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
+// GetName returns the name of the flag.
|
|
|
func (f DurationFlag) GetName() string {
|
|
|
return f.Name
|
|
|
}
|
|
|
@@ -473,11 +473,12 @@ type Float64Flag struct {
|
|
|
Usage string
|
|
|
EnvVar string
|
|
|
Destination *float64
|
|
|
+ Hidden bool
|
|
|
}
|
|
|
|
|
|
// String returns the usage
|
|
|
func (f Float64Flag) String() string {
|
|
|
- return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage))
|
|
|
+ return FlagStringer(f)
|
|
|
}
|
|
|
|
|
|
// Apply populates the flag given the flag set and environment
|
|
|
@@ -503,10 +504,21 @@ func (f Float64Flag) Apply(set *flag.FlagSet) {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
+// GetName returns the name of the flag.
|
|
|
func (f Float64Flag) GetName() string {
|
|
|
return f.Name
|
|
|
}
|
|
|
|
|
|
+func visibleFlags(fl []Flag) []Flag {
|
|
|
+ visible := []Flag{}
|
|
|
+ for _, flag := range fl {
|
|
|
+ if !reflect.ValueOf(flag).FieldByName("Hidden").Bool() {
|
|
|
+ visible = append(visible, flag)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return visible
|
|
|
+}
|
|
|
+
|
|
|
func prefixFor(name string) (prefix string) {
|
|
|
if len(name) == 1 {
|
|
|
prefix = "-"
|
|
|
@@ -517,16 +529,37 @@ func prefixFor(name string) (prefix string) {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
-func prefixedNames(fullName string) (prefixed string) {
|
|
|
+// Returns the placeholder, if any, and the unquoted usage string.
|
|
|
+func unquoteUsage(usage string) (string, string) {
|
|
|
+ for i := 0; i < len(usage); i++ {
|
|
|
+ if usage[i] == '`' {
|
|
|
+ for j := i + 1; j < len(usage); j++ {
|
|
|
+ if usage[j] == '`' {
|
|
|
+ name := usage[i+1 : j]
|
|
|
+ usage = usage[:i] + name + usage[j+1:]
|
|
|
+ return name, usage
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return "", usage
|
|
|
+}
|
|
|
+
|
|
|
+func prefixedNames(fullName, placeholder string) string {
|
|
|
+ var prefixed string
|
|
|
parts := strings.Split(fullName, ",")
|
|
|
for i, name := range parts {
|
|
|
name = strings.Trim(name, " ")
|
|
|
prefixed += prefixFor(name) + name
|
|
|
+ if placeholder != "" {
|
|
|
+ prefixed += " " + placeholder
|
|
|
+ }
|
|
|
if i < len(parts)-1 {
|
|
|
prefixed += ", "
|
|
|
}
|
|
|
}
|
|
|
- return
|
|
|
+ return prefixed
|
|
|
}
|
|
|
|
|
|
func withEnvHint(envVar, str string) string {
|
|
|
@@ -544,3 +577,83 @@ func withEnvHint(envVar, str string) string {
|
|
|
}
|
|
|
return str + envText
|
|
|
}
|
|
|
+
|
|
|
+func stringifyFlag(f Flag) string {
|
|
|
+ fv := reflect.ValueOf(f)
|
|
|
+
|
|
|
+ switch f.(type) {
|
|
|
+ case IntSliceFlag:
|
|
|
+ return withEnvHint(fv.FieldByName("EnvVar").String(),
|
|
|
+ stringifyIntSliceFlag(f.(IntSliceFlag)))
|
|
|
+ case StringSliceFlag:
|
|
|
+ return withEnvHint(fv.FieldByName("EnvVar").String(),
|
|
|
+ stringifyStringSliceFlag(f.(StringSliceFlag)))
|
|
|
+ }
|
|
|
+
|
|
|
+ placeholder, usage := unquoteUsage(fv.FieldByName("Usage").String())
|
|
|
+
|
|
|
+ needsPlaceholder := false
|
|
|
+ defaultValueString := ""
|
|
|
+ val := fv.FieldByName("Value")
|
|
|
+
|
|
|
+ if val.IsValid() {
|
|
|
+ needsPlaceholder = true
|
|
|
+ defaultValueString = fmt.Sprintf(" (default: %v)", val.Interface())
|
|
|
+
|
|
|
+ if val.Kind() == reflect.String && val.String() != "" {
|
|
|
+ defaultValueString = fmt.Sprintf(" (default: %q)", val.String())
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if defaultValueString == " (default: )" {
|
|
|
+ defaultValueString = ""
|
|
|
+ }
|
|
|
+
|
|
|
+ if needsPlaceholder && placeholder == "" {
|
|
|
+ placeholder = defaultPlaceholder
|
|
|
+ }
|
|
|
+
|
|
|
+ usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultValueString))
|
|
|
+
|
|
|
+ return withEnvHint(fv.FieldByName("EnvVar").String(),
|
|
|
+ fmt.Sprintf("%s\t%s", prefixedNames(fv.FieldByName("Name").String(), placeholder), usageWithDefault))
|
|
|
+}
|
|
|
+
|
|
|
+func stringifyIntSliceFlag(f IntSliceFlag) string {
|
|
|
+ defaultVals := []string{}
|
|
|
+ if f.Value != nil && len(f.Value.Value()) > 0 {
|
|
|
+ for _, i := range f.Value.Value() {
|
|
|
+ defaultVals = append(defaultVals, fmt.Sprintf("%d", i))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return stringifySliceFlag(f.Usage, f.Name, defaultVals)
|
|
|
+}
|
|
|
+
|
|
|
+func stringifyStringSliceFlag(f StringSliceFlag) string {
|
|
|
+ defaultVals := []string{}
|
|
|
+ if f.Value != nil && len(f.Value.Value()) > 0 {
|
|
|
+ for _, s := range f.Value.Value() {
|
|
|
+ if len(s) > 0 {
|
|
|
+ defaultVals = append(defaultVals, fmt.Sprintf("%q", s))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return stringifySliceFlag(f.Usage, f.Name, defaultVals)
|
|
|
+}
|
|
|
+
|
|
|
+func stringifySliceFlag(usage, name string, defaultVals []string) string {
|
|
|
+ placeholder, usage := unquoteUsage(usage)
|
|
|
+ if placeholder == "" {
|
|
|
+ placeholder = defaultPlaceholder
|
|
|
+ }
|
|
|
+
|
|
|
+ defaultVal := ""
|
|
|
+ if len(defaultVals) > 0 {
|
|
|
+ defaultVal = fmt.Sprintf(" (default: %s)", strings.Join(defaultVals, ", "))
|
|
|
+ }
|
|
|
+
|
|
|
+ usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultVal))
|
|
|
+ return fmt.Sprintf("%s\t%s", prefixedNames(name, placeholder), usageWithDefault)
|
|
|
+}
|