diff --git a/flag_set.go b/flag_set.go index 4107631..39388e4 100644 --- a/flag_set.go +++ b/flag_set.go @@ -458,16 +458,17 @@ func (cfg FlagConfig) getPlaceholder() string { } // Otherwise, use a transformation of the flag value type name. - var typeName string - { - typeName = strings.ToUpper(fmt.Sprintf("%T", cfg.Value)) - typeName = genericTypeNameRegexp.ReplaceAllString(typeName, "$1") - typeName = strings.TrimSuffix(typeName, "VALUE") - if lastDot := strings.LastIndex(typeName, "."); lastDot > 0 { - typeName = typeName[lastDot+1:] - } + return reflectPlaceholder(cfg.Value) +} + +func reflectPlaceholder(v flag.Value) (typeName string) { + typeName = strings.ToUpper(fmt.Sprintf("%T", v)) + typeName = genericTypeNameRegexp.ReplaceAllString(typeName, "$1") + typeName = strings.TrimSuffix(typeName, "VALUE") + if lastDot := strings.LastIndex(typeName, "."); lastDot > 0 { + typeName = typeName[lastDot+1:] } - return typeName + return } func (cfg FlagConfig) getHelpDefault() string { @@ -721,7 +722,7 @@ func (fs *FlagSet) AddStruct(val any) error { ) if fieldValAddrTyp.Implements(flagValueElemTyp) { // The field implements flag.Value, we can use it directly. - cfg.Value = fieldValAddrIface.(flag.Value) + cfg.Value = &defaultValue{Value: fieldValAddrIface.(flag.Value), def: def} } else { // Try to construct a new flag value. v, err := ffval.NewValueReflect(fieldValAddrIface, def) @@ -746,6 +747,34 @@ func (fs *FlagSet) AddStruct(val any) error { return nil } +type defaultValue struct { + flag.Value + + isSet bool + def string +} + +func (d *defaultValue) GetPlaceholder() string { + return reflectPlaceholder(d.Value) +} + +func (d *defaultValue) String() string { + if d.isSet { + return d.Value.String() + } + + return d.def +} + +func (d *defaultValue) Set(s string) error { + if err := d.Value.Set(s); err != nil { + return err + } + + d.isSet = true + return nil +} + // Value defines a new flag in the flag set, and panics on any error. func (fs *FlagSet) Value(short rune, long string, value flag.Value, usage string) Flag { f, err := fs.AddFlag(FlagConfig{ diff --git a/flag_set_test.go b/flag_set_test.go index 928c6d7..6dfe1ae 100644 --- a/flag_set_test.go +++ b/flag_set_test.go @@ -317,9 +317,10 @@ func TestFlagSet_structs(t *testing.T) { Beta int `ff:" long: beta, placeholder: β, usage: beta int"` Delta bool `ff:"short: d, nodefault, usage: delta bool"` - Epsilon bool `ff:"| short=e | long=epsilon | nodefault | usage: epsilon bool |"` - Gamma string `ff:"| short=g | long=gamma | | usage: 'usage, with a comma' |"` - Iota float64 `ff:"| | long=iota | default=0.43 | usage: iota float |"` + Epsilon bool `ff:"| short=e | long=epsilon | nodefault | usage: epsilon bool |"` + Gamma string `ff:"| short=g | long=gamma | | usage: 'usage, with a comma' |"` + Iota float64 `ff:"| | long=iota | default=0.43 | usage: iota float |"` + Value customValue `ff:"| | long=value | default=abcd | usage: some value implementation |"` } var flags myFlags @@ -336,6 +337,7 @@ func TestFlagSet_structs(t *testing.T) { -e, --epsilon epsilon bool -g, --gamma STRING usage, with a comma --iota FLOAT64 iota float (default: 0.43) + --value CUSTOM some value implementation (default: abcd) `), fftest.UnindentString(ffhelp.Flags(fs).String()); want != have { t.Error(fftest.DiffString(want, have)) } @@ -346,15 +348,19 @@ func TestFlagSet_structs(t *testing.T) { }{ { args: "--alpha=x", - want: myFlags{Alpha: "x", Iota: 0.43}, + want: myFlags{Alpha: "x", Iota: 0.43, Value: "abcd"}, }, { args: "-e --iota=1.23", - want: myFlags{Alpha: "alpha-default", Epsilon: true, Iota: 1.23}, + want: myFlags{Alpha: "alpha-default", Epsilon: true, Iota: 1.23, Value: "abcd"}, }, { args: "-gabc -d", - want: myFlags{Alpha: "alpha-default", Delta: true, Gamma: "abc", Iota: 0.43}, + want: myFlags{Alpha: "alpha-default", Delta: true, Gamma: "abc", Iota: 0.43, Value: "abcd"}, + }, + { + args: "--value=bcde", + want: myFlags{Alpha: "alpha-default", Iota: 0.43, Value: "bcde"}, }, } { t.Run(testcase.args, func(t *testing.T) { @@ -593,3 +599,14 @@ func TestFlagSet_Std(t *testing.T) { t.Errorf("flag names: want %v, have %v", want, have) } } + +type customValue string + +func (v *customValue) String() string { + return (string)(*v) +} + +func (v *customValue) Set(s string) error { + *v = customValue(s) + return nil +}