From acdde9d304c663040ef66d3f1ee1990b294c29e8 Mon Sep 17 00:00:00 2001 From: Vincent Boutour Date: Sun, 16 Apr 2023 16:04:31 +0200 Subject: [PATCH] feat(flag)!: Adding shorthand support Signed-off-by: Vincent Boutour --- cmd/advanced/advanced.go | 4 +-- cmd/simple/simple.go | 2 +- flag.go | 62 +++++++++++++++++++++++++++++++++------- flag_test.go | 10 +++---- 4 files changed, 59 insertions(+), 19 deletions(-) diff --git a/cmd/advanced/advanced.go b/cmd/advanced/advanced.go index 31ea88d..56c3bb1 100644 --- a/cmd/advanced/advanced.go +++ b/cmd/advanced/advanced.go @@ -19,9 +19,9 @@ type databaseConfig struct { func databaseFlags(fs *flag.FlagSet, prefix string, overrides ...flags.Override) databaseConfig { return databaseConfig{ - url: flags.String(fs, prefix, "db", "Url", "Database url", "", overrides), + url: flags.String(fs, prefix, "db", "Url", "", "Database url", "", overrides), port: flags.Uint(fs, prefix, "db", "Port", "Database port", 5432, overrides), - name: flags.String(fs, prefix, "db", "Name", "Database name", "user", overrides), + name: flags.String(fs, prefix, "db", "Name", "", "Database name", "user", overrides), timeout: flags.Duration(fs, prefix, "db", "Timeout", "Request timeout", time.Second, overrides), } } diff --git a/cmd/simple/simple.go b/cmd/simple/simple.go index 57c470d..09bab94 100644 --- a/cmd/simple/simple.go +++ b/cmd/simple/simple.go @@ -20,7 +20,7 @@ import ( func main() { fs := flag.NewFlagSet("my-cli", flag.ExitOnError) - address := flags.String(fs, "exemple", "server", "Address", "Listen address", "", nil) + address := flags.String(fs, "exemple", "server", "Address", "a", "Listen address", "", nil) port := flags.Uint(fs, "exemple", "server", "Port", "Listen port (0 to disable)", 1080, nil) headers := flags.StringSlice(fs, "exemple", "server", "Header", "Header to add", []string{"x-user", "x-auth"}, nil) diff --git a/flag.go b/flag.go index 43fe690..0eff646 100644 --- a/flag.go +++ b/flag.go @@ -10,89 +10,129 @@ import ( ) // String creates a string flag -func String(fs *flag.FlagSet, prefix, docPrefix, name, label string, value string, overrides []Override) *string { +func String(fs *flag.FlagSet, prefix, docPrefix, name, shorthand, label string, value string, overrides []Override) *string { flagName, envName := getNameAndEnv(fs, FirstUpperCase(prefix), name) output := new(string) + initialValue := defaultValue(defaultStaticValue(name, value, overrides), envName, func(input string) (string, error) { return input, nil }) + if len(shorthand) > 0 { + fs.StringVar(output, FirstLowerCase(prefix+FirstUpperCase(shorthand)), initialValue, fmt.Sprintf("Shorthand for -%s", FirstLowerCase(flagName))) + } + fs.StringVar(output, FirstLowerCase(flagName), initialValue, formatLabel(prefix, docPrefix, label, envName)) return output } // Int creates an int flag -func Int(fs *flag.FlagSet, prefix, docPrefix, name, label string, value int, overrides []Override) *int { +func Int(fs *flag.FlagSet, prefix, docPrefix, name, shorthand, label string, value int, overrides []Override) *int { flagName, envName := getNameAndEnv(fs, FirstUpperCase(prefix), name) + output := new(int) + initialValue := defaultValue(defaultStaticValue(name, value, overrides), envName, func(input string) (int, error) { intVal, err := strconv.ParseInt(input, 10, 32) return int(intVal), err }) - return fs.Int(FirstLowerCase(flagName), initialValue, formatLabel(prefix, docPrefix, label, envName)) + if len(shorthand) > 0 { + fs.IntVar(output, FirstLowerCase(prefix+FirstUpperCase(shorthand)), initialValue, fmt.Sprintf("Shorthand for -%s", FirstLowerCase(flagName))) + } + + fs.IntVar(output, FirstLowerCase(flagName), initialValue, formatLabel(prefix, docPrefix, label, envName)) + + return output } // Int64 creates an int64 flag func Int64(fs *flag.FlagSet, prefix, docPrefix, name, label string, value int64, overrides []Override) *int64 { flagName, envName := getNameAndEnv(fs, FirstUpperCase(prefix), name) + output := new(int64) + initialValue := defaultValue(defaultStaticValue(name, value, overrides), envName, func(input string) (int64, error) { return strconv.ParseInt(input, 10, 64) }) - return fs.Int64(FirstLowerCase(flagName), initialValue, formatLabel(prefix, docPrefix, label, envName)) + fs.Int64Var(output, FirstLowerCase(flagName), initialValue, formatLabel(prefix, docPrefix, label, envName)) + + return output } // Uint creates an uint flag func Uint(fs *flag.FlagSet, prefix, docPrefix, name, label string, value uint, overrides []Override) *uint { flagName, envName := getNameAndEnv(fs, FirstUpperCase(prefix), name) + output := new(uint) + initialValue := defaultValue(defaultStaticValue(name, value, overrides), envName, func(input string) (uint, error) { intVal, err := strconv.ParseUint(input, 10, 32) return uint(intVal), err }) - return fs.Uint(FirstLowerCase(flagName), initialValue, formatLabel(prefix, docPrefix, label, envName)) + fs.UintVar(output, FirstLowerCase(flagName), initialValue, formatLabel(prefix, docPrefix, label, envName)) + + return output } // Uint64 creates an uint64 flag func Uint64(fs *flag.FlagSet, prefix, docPrefix, name, label string, value uint64, overrides []Override) *uint64 { flagName, envName := getNameAndEnv(fs, FirstUpperCase(prefix), name) - return fs.Uint64(FirstLowerCase(flagName), defaultValue(defaultStaticValue(name, value, overrides), envName, func(input string) (uint64, error) { + + output := new(uint64) + + initialValue := defaultValue(defaultStaticValue(name, value, overrides), envName, func(input string) (uint64, error) { return strconv.ParseUint(input, 10, 64) - }), formatLabel(prefix, docPrefix, label, envName)) + }) + + fs.Uint64Var(output, FirstLowerCase(flagName), initialValue, formatLabel(prefix, docPrefix, label, envName)) + + return output } // Float64 creates a float64 flag func Float64(fs *flag.FlagSet, prefix, docPrefix, name, label string, value float64, overrides []Override) *float64 { flagName, envName := getNameAndEnv(fs, FirstUpperCase(prefix), name) + output := new(float64) + initialValue := defaultValue(defaultStaticValue(name, value, overrides), envName, func(input string) (float64, error) { return strconv.ParseFloat(input, 64) }) - return fs.Float64(FirstLowerCase(flagName), initialValue, formatLabel(prefix, docPrefix, label, envName)) + fs.Float64Var(output, FirstLowerCase(flagName), initialValue, formatLabel(prefix, docPrefix, label, envName)) + + return output } // Bool creates a bool flag func Bool(fs *flag.FlagSet, prefix, docPrefix, name, label string, value bool, overrides []Override) *bool { flagName, envName := getNameAndEnv(fs, FirstUpperCase(prefix), name) + output := new(bool) + initialValue := defaultValue(defaultStaticValue(name, value, overrides), envName, strconv.ParseBool) - return fs.Bool(FirstLowerCase(flagName), initialValue, formatLabel(prefix, docPrefix, label, envName)) + fs.BoolVar(output, FirstLowerCase(flagName), initialValue, formatLabel(prefix, docPrefix, label, envName)) + + return output } // Duration creates a duration flag func Duration(fs *flag.FlagSet, prefix, docPrefix, name, label string, value time.Duration, overrides []Override) *time.Duration { flagName, envName := getNameAndEnv(fs, FirstUpperCase(prefix), name) + output := new(time.Duration) + initialValue := defaultValue(defaultStaticValue(name, value, overrides), envName, time.ParseDuration) - return fs.Duration(FirstLowerCase(flagName), initialValue, formatLabel(prefix, docPrefix, label, envName)) + fs.DurationVar(output, FirstLowerCase(flagName), initialValue, formatLabel(prefix, docPrefix, label, envName)) + + return output } type stringSlice struct { @@ -144,7 +184,7 @@ func StringSlice(fs *flag.FlagSet, prefix, docPrefix, name, label string, value p := new([]string) - fs.Var(newStringSlice(initialValue, p), FirstLowerCase(flagName), formatLabel(prefix, docPrefix, label, envName)) + fs.Var(newStringSlice(initialValue, p), FirstLowerCase(flagName), formatLabel(prefix, docPrefix, label, envName)+"\n\t `string slice`, environment value should be comma separated") return p } diff --git a/flag_test.go b/flag_test.go index 97bffdd..3ab649d 100644 --- a/flag_test.go +++ b/flag_test.go @@ -63,7 +63,7 @@ func TestString(t *testing.T) { for intention, tc := range cases { t.Run(intention, func(t *testing.T) { fs := flag.NewFlagSet("String", flag.ContinueOnError) - String(fs, tc.prefix, tc.docPrefix, tc.name, tc.label, tc.defaultValue, tc.overrides) + String(fs, tc.prefix, tc.docPrefix, tc.name, "", tc.label, tc.defaultValue, tc.overrides) var writer strings.Builder fs.SetOutput(&writer) @@ -125,7 +125,7 @@ func TestInt(t *testing.T) { for intention, tc := range cases { t.Run(intention, func(t *testing.T) { fs := flag.NewFlagSet("Int", flag.ContinueOnError) - Int(fs, tc.prefix, tc.docPrefix, tc.name, tc.label, tc.defaultValue, nil) + Int(fs, tc.prefix, tc.docPrefix, tc.name, "", tc.label, tc.defaultValue, nil) var writer strings.Builder fs.SetOutput(&writer) @@ -541,7 +541,7 @@ func TestStringSlice(t *testing.T) { "test", nil, "Test flag", - "Usage of Values:\n -test value\n \t[cli] Test flag {VALUES_TEST}\n", + "Usage of Values:\n -test string slice\n \t[cli] Test flag {VALUES_TEST}\n \t\t string slice, environment value should be comma separated\n", }, "with prefix": { "context", @@ -549,7 +549,7 @@ func TestStringSlice(t *testing.T) { "test", []string{"value"}, "Test flag", - "Usage of Values:\n -contextTest value\n \t[context] Test flag {VALUES_CONTEXT_TEST} (default [value])\n", + "Usage of Values:\n -contextTest string slice\n \t[context] Test flag {VALUES_CONTEXT_TEST}\n \t\t string slice, environment value should be comma separated (default [value])\n", }, "env": { "", @@ -557,7 +557,7 @@ func TestStringSlice(t *testing.T) { "value", []string{"value"}, "Test flag", - "Usage of Values:\n -value value\n \t[cli] Test flag {VALUES_VALUE} (default [overriden])\n", + "Usage of Values:\n -value string slice\n \t[cli] Test flag {VALUES_VALUE}\n \t\t string slice, environment value should be comma separated (default [overriden])\n", }, }