package goflags import ( "errors" "fmt" "reflect" "strconv" "strings" ) type dynamicFlag struct { field interface{} defaultValue interface{} name string } func (df *dynamicFlag) Set(value string) error { fieldKind := reflect.TypeOf(df.field).Elem().Kind() var ( optionWithoutValue bool pv bool ) if value == "t" || value == "T" || value == "true" || value == "TRUE" { pv = true optionWithoutValue = true } else if value == "f" || value == "F" || value == "false" || value == "FALSE" { pv = false } switch fieldKind { case reflect.Bool: boolField := df.field.(*bool) *boolField = pv case reflect.Int: intField := df.field.(*int) if optionWithoutValue { *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 optionWithoutValue { *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 optionWithoutValue { *stringField = df.defaultValue.(string) return nil } *stringField = value case reflect.Slice: sliceField := df.field.(*[]string) if optionWithoutValue { *sliceField = df.defaultValue.([]string) return nil } *sliceField = append(*sliceField, strings.Split(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 }