Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support default for flag.Value implementations registered via AddStruct
Browse files Browse the repository at this point in the history
GeorgeMac committed Jun 14, 2024
1 parent 4e42878 commit fb812df
Showing 2 changed files with 62 additions and 16 deletions.
49 changes: 39 additions & 10 deletions flag_set.go
Original file line number Diff line number Diff line change
@@ -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{
29 changes: 23 additions & 6 deletions flag_set_test.go
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit fb812df

Please sign in to comment.