From 97fd93272f0a480907246cf41b109bb81c3e01ee Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Fri, 11 Jul 2014 13:29:56 -0400 Subject: [PATCH] Starting to hack in some env var configuration goodness --- flag.go | 161 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 126 insertions(+), 35 deletions(-) diff --git a/flag.go b/flag.go index e6f8838..50f192f 100644 --- a/flag.go +++ b/flag.go @@ -3,18 +3,19 @@ package cli import ( "flag" "fmt" + "os" "strconv" "strings" ) // This flag enables bash-completion for all commands and subcommands -var BashCompletionFlag = BoolFlag{"generate-bash-completion", ""} +var BashCompletionFlag = BoolFlag{"generate-bash-completion", "", ""} // This flag prints the version for the application -var VersionFlag = BoolFlag{"version, v", "print the version"} +var VersionFlag = BoolFlag{"version, v", "print the version", ""} // This flag prints the help for all commands and subcommands -var HelpFlag = BoolFlag{"help, h", "show help"} +var HelpFlag = BoolFlag{"help, h", "show help", ""} // Flag is a common interface related to parsing flags in cli. // For more advanced flag parsing techniques, it is recomended that @@ -51,16 +52,24 @@ type Generic interface { // GenericFlag is the flag type for types implementing Generic type GenericFlag struct { - Name string - Value Generic - Usage string + Name string + Value Generic + Usage string + EnvVar string } func (f GenericFlag) String() string { - return fmt.Sprintf("%s%s %v\t`%v` %s", prefixFor(f.Name), f.Name, f.Value, "-"+f.Name+" option -"+f.Name+" option", f.Usage) + return withEnvHint(f.EnvVar, fmt.Sprintf("%s%s %v\t`%v` %s", prefixFor(f.Name), f.Name, f.Value, "-"+f.Name+" option -"+f.Name+" option", f.Usage)) } func (f GenericFlag) Apply(set *flag.FlagSet) { + val := f.Value + if f.EnvVar != "" { + if envVal := os.Getenv(f.EnvVar); envVal != "" { + val.Set(envVal) + } + } + eachName(f.Name, func(name string) { set.Var(f.Value, name, f.Usage) }) @@ -86,18 +95,29 @@ func (f *StringSlice) Value() []string { } type StringSliceFlag struct { - Name string - Value *StringSlice - Usage string + Name string + Value *StringSlice + Usage string + EnvVar string } func (f StringSliceFlag) String() string { firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ") pref := prefixFor(firstName) - return fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage) + return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage)) } func (f StringSliceFlag) Apply(set *flag.FlagSet) { + if f.EnvVar != "" { + if envVal := os.Getenv(f.EnvVar); envVal != "" { + newVal := &StringSlice{} + for _, s := range strings.Split(envVal, ",") { + newVal.Set(s) + } + f.Value = newVal + } + } + eachName(f.Name, func(name string) { set.Var(f.Value, name, f.Usage) }) @@ -129,18 +149,32 @@ func (f *IntSlice) Value() []int { } type IntSliceFlag struct { - Name string - Value *IntSlice - Usage string + Name string + Value *IntSlice + Usage string + EnvVar string } func (f IntSliceFlag) String() string { firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ") pref := prefixFor(firstName) - return fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage) + return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage)) } func (f IntSliceFlag) Apply(set *flag.FlagSet) { + if f.EnvVar != "" { + if envVal := os.Getenv(f.EnvVar); envVal != "" { + newVal := &IntSlice{} + for _, s := range strings.Split(envVal, ",") { + err := newVal.Set(s) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + } + } + f.Value = newVal + } + } + eachName(f.Name, func(name string) { set.Var(f.Value, name, f.Usage) }) @@ -151,17 +185,28 @@ func (f IntSliceFlag) getName() string { } type BoolFlag struct { - Name string - Usage string + Name string + Usage string + EnvVar string } func (f BoolFlag) String() string { - return fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage) + return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage)) } func (f BoolFlag) Apply(set *flag.FlagSet) { + val := false + if f.EnvVar != "" { + if envVal := os.Getenv(f.EnvVar); envVal != "" { + envValBool, err := strconv.ParseBool(envVal) + if err == nil { + val = envValBool + } + } + } + eachName(f.Name, func(name string) { - set.Bool(name, false, f.Usage) + set.Bool(name, val, f.Usage) }) } @@ -170,17 +215,28 @@ func (f BoolFlag) getName() string { } type BoolTFlag struct { - Name string - Usage string + Name string + Usage string + EnvVar string } func (f BoolTFlag) String() string { - return fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage) + return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage)) } func (f BoolTFlag) Apply(set *flag.FlagSet) { + val := true + if f.EnvVar != "" { + if envVal := os.Getenv(f.EnvVar); envVal != "" { + envValBool, err := strconv.ParseBool(envVal) + if err == nil { + val = envValBool + } + } + } + eachName(f.Name, func(name string) { - set.Bool(name, true, f.Usage) + set.Bool(name, val, f.Usage) }) } @@ -189,9 +245,10 @@ func (f BoolTFlag) getName() string { } type StringFlag struct { - Name string - Value string - Usage string + Name string + Value string + Usage string + EnvVar string } func (f StringFlag) String() string { @@ -204,10 +261,16 @@ func (f StringFlag) String() string { fmtString = "%s %v\t%v" } - return fmt.Sprintf(fmtString, prefixedNames(f.Name), f.Value, f.Usage) + return withEnvHint(f.EnvVar, fmt.Sprintf(fmtString, prefixedNames(f.Name), f.Value, f.Usage)) } func (f StringFlag) Apply(set *flag.FlagSet) { + if f.EnvVar != "" { + if envVal := os.Getenv(f.EnvVar); envVal != "" { + f.Value = envVal + } + } + eachName(f.Name, func(name string) { set.String(name, f.Value, f.Usage) }) @@ -218,16 +281,26 @@ func (f StringFlag) getName() string { } type IntFlag struct { - Name string - Value int - Usage string + Name string + Value int + Usage string + EnvVar string } func (f IntFlag) String() string { - return fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage) + return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage)) } func (f IntFlag) Apply(set *flag.FlagSet) { + if f.EnvVar != "" { + if envVal := os.Getenv(f.EnvVar); envVal != "" { + envValInt, err := strconv.ParseUint(envVal, 10, 64) + if err == nil { + f.Value = int(envValInt) + } + } + } + eachName(f.Name, func(name string) { set.Int(name, f.Value, f.Usage) }) @@ -238,16 +311,26 @@ func (f IntFlag) getName() string { } type Float64Flag struct { - Name string - Value float64 - Usage string + Name string + Value float64 + Usage string + EnvVar string } func (f Float64Flag) String() string { - return fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage) + return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage)) } func (f Float64Flag) Apply(set *flag.FlagSet) { + if f.EnvVar != "" { + if envVal := os.Getenv(f.EnvVar); envVal != "" { + envValFloat, err := strconv.ParseFloat(envVal, 10) + if err == nil { + f.Value = float64(envValFloat) + } + } + } + eachName(f.Name, func(name string) { set.Float64(name, f.Value, f.Usage) }) @@ -278,3 +361,11 @@ func prefixedNames(fullName string) (prefixed string) { } return } + +func withEnvHint(envVar, str string) string { + envText := "" + if envVar != "" { + envText = fmt.Sprintf(" [$%s]", envVar) + } + return str + envText +}