Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 39 additions & 10 deletions flag_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand All @@ -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{
Expand Down
29 changes: 23 additions & 6 deletions flag_set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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))
}
Expand All @@ -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) {
Expand Down Expand Up @@ -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
}