From a634ac4b5c5df61f5925a557b6855094190a13a2 Mon Sep 17 00:00:00 2001 From: Ramana Reddy Date: Thu, 10 Aug 2023 01:00:30 +0530 Subject: [PATCH] add flag support act as flag with defaultValue or option with value --- dynamic_var.go | 114 +++++++++++++++++++++++++++++++++++++++++ examples/basic/main.go | 11 ++++ 2 files changed, 125 insertions(+) create mode 100644 dynamic_var.go diff --git a/dynamic_var.go b/dynamic_var.go new file mode 100644 index 0000000..b5e69b2 --- /dev/null +++ b/dynamic_var.go @@ -0,0 +1,114 @@ +package goflags + +import ( + "errors" + "fmt" + "reflect" + "strconv" +) + +type dynamicFlag struct { + field interface{} + defaultValue interface{} + name string +} + +func (df *dynamicFlag) Set(value string) error { + fieldKind := reflect.TypeOf(df.field).Elem().Kind() + var isBoolValue bool + if _, err := strconv.ParseBool(value); err == nil { + isBoolValue = true + } + if fieldKind == reflect.Bool && isBoolValue { + boolField := df.field.(*bool) + *boolField = true + return nil + } + switch fieldKind { + case reflect.Int: + intField := df.field.(*int) + if isBoolValue { + *intField = df.defaultValue.(int) + return nil + } + newValue, err := strconv.Atoi(value) + if err != nil { + return err + } + *intField = newValue + case reflect.Float64: + floatField := df.field.(*float64) + if isBoolValue { + *floatField = df.defaultValue.(float64) + return nil + } + newValue, err := strconv.ParseFloat(value, 64) + if err != nil { + return err + } + *floatField = newValue + case reflect.String: + stringField := df.field.(*string) + if isBoolValue { + *stringField = df.defaultValue.(string) + return nil + } + *stringField = value + default: + return errors.New("unsupported type") + } + return nil +} + +func (df *dynamicFlag) IsBoolFlag() bool { + return true +} + +func (df *dynamicFlag) String() string { + return df.name +} + +// DynamicVar acts as flag with a default value or a option with value +// example: +// var titleSize int +// flagSet.DynamicVar(&titleSize, "title", 50, "first N characters of the title") +// > go run ./examples/basic -title or go run ./examples/basic -title=100 +// In case of `go run ./examples/basic -title` it will use default value 50 +func (flagSet *FlagSet) DynamicVar(field interface{}, long string, defaultValue interface{}, usage string) *FlagData { + return flagSet.DynamicVarP(field, long, "", defaultValue, usage) +} + +// DynamicVarP same as DynamicVar but with short name +func (flagSet *FlagSet) DynamicVarP(field interface{}, long, short string, defaultValue interface{}, usage string) *FlagData { + // validate field and defaultValue + if reflect.TypeOf(field).Kind() != reflect.Ptr { + panic(fmt.Errorf("-%v flag field must be a pointer", long)) + } + if reflect.TypeOf(field).Elem().Kind() != reflect.TypeOf(defaultValue).Kind() { + panic(fmt.Errorf("-%v flag field and defaultValue mismatch: fied type is %v and defaultValue Type is %T", long, reflect.TypeOf(field).Elem().Kind(), defaultValue)) + } + if field == nil { + panic(fmt.Errorf("field cannot be nil for flag -%v", long)) + } + + var dynamicFlag dynamicFlag + dynamicFlag.field = field + dynamicFlag.name = long + if defaultValue != nil { + dynamicFlag.defaultValue = defaultValue + } + + flagData := &FlagData{ + usage: usage, + long: long, + defaultValue: defaultValue, + } + if short != "" { + flagData.short = short + flagSet.CommandLine.Var(&dynamicFlag, short, usage) + flagSet.flagKeys.Set(short, flagData) + } + flagSet.CommandLine.Var(&dynamicFlag, long, usage) + flagSet.flagKeys.Set(long, flagData) + return flagData +} diff --git a/examples/basic/main.go b/examples/basic/main.go index e7f3ba1..2a10776 100644 --- a/examples/basic/main.go +++ b/examples/basic/main.go @@ -17,6 +17,9 @@ type Options struct { duration time.Duration rls goflags.RateLimitMap severity []string + // Dynamic + titleSize int + target string } func main() { @@ -40,12 +43,20 @@ func main() { flagSet.DurationVar(&testOptions.duration, "timeout", time.Hour, "timeout"), flagSet.EnumSliceVarP(&testOptions.severity, "severity", "s", []goflags.EnumVariable{2}, "severity of the scan", goflags.AllowdTypes{"low": goflags.EnumVariable(0), "medium": goflags.EnumVariable(1), "high": goflags.EnumVariable(2)}), ) + flagSet.CreateGroup("Dynmaic", "Dynamic", + flagSet.DynamicVarP(&testOptions.titleSize, "title", "t", 50, "first N characters of the title"), + flagSet.DynamicVarP(&testOptions.target, "target", "u", "https://example.com", "target url"), + ) flagSet.SetCustomHelpText("EXAMPLE USAGE:\ngo run ./examples/basic [OPTIONS]") if err := flagSet.Parse(); err != nil { log.Fatal(err) } + // TODO: remove this + fmt.Println("title size:", testOptions.titleSize) + fmt.Println("target:", testOptions.target) + // ratelimits value is if len(testOptions.rls.AsMap()) > 0 { fmt.Printf("Got RateLimits: %+v\n", testOptions.rls)