Skip to content

Commit

Permalink
Fix:(issue_2032) Support for post parse config loading
Browse files Browse the repository at this point in the history
  • Loading branch information
dearchap committed Dec 25, 2024
1 parent cf01a1d commit 13b74ee
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 16 deletions.
6 changes: 6 additions & 0 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,12 @@ func (cmd *Command) Run(ctx context.Context, osArgs []string) (deferErr error) {
return nil
}

for _, flag := range cmd.Flags {
if err := flag.PostParse(); err != nil {
return err
}
}

if cmd.After != nil && !cmd.Root().shellCompletion {
defer func() {
if err := cmd.After(ctx, cmd); err != nil {
Expand Down
4 changes: 4 additions & 0 deletions command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2306,6 +2306,10 @@ func (c *customBoolFlag) GetUsage() string {
return "usage"
}

func (c *customBoolFlag) PostParse() error {
return nil
}

func (c *customBoolFlag) Apply(set *flag.FlagSet) error {
set.String(c.Nombre, c.Nombre, "")
return nil
Expand Down
2 changes: 2 additions & 0 deletions flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ type ActionableFlag interface {
type Flag interface {
fmt.Stringer

PostParse() error

// Apply Flag settings to the given flag set
Apply(*flag.FlagSet) error

Expand Down
14 changes: 14 additions & 0 deletions flag_bool_with_inverse.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,20 @@ func (parent *BoolWithInverseFlag) inverseAliases() (aliases []string) {
return
}

func (parent *BoolWithInverseFlag) PostParse() error {
if parent.positiveFlag != nil {
if err := parent.positiveFlag.PostParse(); err != nil {
return err
}
}
if parent.negativeFlag != nil {
if err := parent.negativeFlag.PostParse(); err != nil {
return err
}
}
return nil
}

func (parent *BoolWithInverseFlag) Apply(set *flag.FlagSet) error {
if parent.positiveFlag == nil {
parent.initialize()
Expand Down
4 changes: 4 additions & 0 deletions flag_ext.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ type extFlag struct {
f *flag.Flag
}

func (e *extFlag) PostParse() error {
return nil
}

func (e *extFlag) Apply(fs *flag.FlagSet) error {
fs.Var(e.f.Value, e.f.Name, e.f.Usage)
return nil
Expand Down
39 changes: 23 additions & 16 deletions flag_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,35 +98,42 @@ func (f *FlagBase[T, C, V]) GetValue() string {
return fmt.Sprintf("%v", f.Value)
}

// Apply populates the flag given the flag set and environment
func (f *FlagBase[T, C, V]) Apply(set *flag.FlagSet) error {
tracef("apply (flag=%[1]q)", f.Name)

// TODO move this phase into a separate flag initialization function
// if flag has been applied previously then it would have already been set
// from env or file. So no need to apply the env set again. However
// lots of units tests prior to persistent flags assumed that the
// flag can be applied to different flag sets multiple times while still
// keeping the env set.
if !f.applied || f.Local {
newVal := f.Value
// PostParse populates the flag given the flag set and environment
func (f *FlagBase[T, C, V]) PostParse() error {
tracef("postparse (flag=%[1]q)", f.Name)

if !f.hasBeenSet {
if val, source, found := f.Sources.LookupWithSource(); found {
tmpVal := f.creator.Create(f.Value, new(T), f.Config)
if val != "" || reflect.TypeOf(f.Value).Kind() == reflect.String {
if err := tmpVal.Set(val); err != nil {
if err := f.value.Set(val); err != nil {
return fmt.Errorf(
"could not parse %[1]q as %[2]T value from %[3]s for flag %[4]s: %[5]s",
val, f.Value, source, f.Name, err,
)
}
} else if val == "" && reflect.TypeOf(f.Value).Kind() == reflect.Bool {
_ = tmpVal.Set("false")
_ = f.value.Set("false")
}

newVal = tmpVal.Get().(T)
f.hasBeenSet = true
}
}

return nil
}

// Apply populates the flag given the flag set and environment
func (f *FlagBase[T, C, V]) Apply(set *flag.FlagSet) error {
tracef("apply (flag=%[1]q)", f.Name)

// TODO move this phase into a separate flag initialization function
// if flag has been applied previously then it would have already been set
// from env or file. So no need to apply the env set again. However
// lots of units tests prior to persistent flags assumed that the
// flag can be applied to different flag sets multiple times while still
// keeping the env set.
if !f.applied || f.Local {
newVal := f.Value

if f.Destination == nil {
f.value = f.creator.Create(newVal, new(T), f.Config)
Expand Down
19 changes: 19 additions & 0 deletions flag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,8 @@ func TestStringSliceFlagApply_UsesEnvValues_noDefault(t *testing.T) {
_ = fl.Apply(set)

err := set.Parse(nil)

_ = fl.PostParse()
assert.NoError(t, err)
assert.Equal(t, []string{"vincent van goat", "scape goat"}, set.Lookup("goat").Value.(flag.Getter).Get())
}
Expand All @@ -785,6 +787,7 @@ func TestStringSliceFlagApply_UsesEnvValues_withDefault(t *testing.T) {
set := flag.NewFlagSet("test", 0)
_ = fl.Apply(set)
err := set.Parse(nil)
_ = fl.PostParse()
assert.NoError(t, err)
assert.Equal(t, []string{"vincent van goat", "scape goat"}, set.Lookup("goat").Value.(flag.Getter).Get())
}
Expand All @@ -798,6 +801,8 @@ func TestStringSliceFlagApply_DefaultValueWithDestination(t *testing.T) {
_ = fl.Apply(set)

err := set.Parse([]string{})

_ = fl.PostParse()
assert.NoError(t, err)
assert.Equal(t, defValue, dest)
}
Expand Down Expand Up @@ -1056,6 +1061,7 @@ func TestIntSliceFlagApply_UsesEnvValues_noDefault(t *testing.T) {
r := require.New(t)
r.NoError(fl.Apply(set))
r.NoError(set.Parse(nil))
r.NoError(fl.PostParse())
r.Equal([]int64{1, 2}, set.Lookup("goat").Value.(flag.Getter).Get())
}

Expand All @@ -1068,6 +1074,7 @@ func TestIntSliceFlagApply_UsesEnvValues_withDefault(t *testing.T) {
r := require.New(t)
r.NoError(fl.Apply(set))
r.NoError(set.Parse(nil))
r.NoError(fl.PostParse())
r.Equal([]int64{3, 4}, val)
r.Equal([]int64{1, 2}, set.Lookup("goat").Value.(flag.Getter).Get())
}
Expand All @@ -1081,6 +1088,7 @@ func TestIntSliceFlagApply_DefaultValueWithDestination(t *testing.T) {
_ = fl.Apply(set)

err := set.Parse([]string{})
assert.NoError(t, fl.PostParse())
assert.NoError(t, err)
assert.Equal(t, defValue, dest)
}
Expand Down Expand Up @@ -1184,6 +1192,7 @@ func TestUintSliceFlagApply_UsesEnvValues_noDefault(t *testing.T) {
r.NoError(fl.Apply(set))

r.NoError(set.Parse(nil))
r.NoError(fl.PostParse())
r.Equal([]uint64{1, 2}, set.Lookup("goat").Value.(flag.Getter).Get().([]uint64))
}

Expand All @@ -1195,6 +1204,7 @@ func TestUintSliceFlagApply_UsesEnvValues_withDefault(t *testing.T) {
r := require.New(t)
r.NoError(fl.Apply(set))
r.NoError(set.Parse(nil))
r.NoError(fl.PostParse())
r.Equal([]uint64{3, 4}, val.Value())
r.Equal([]uint64{1, 2}, set.Lookup("goat").Value.(flag.Getter).Get().([]uint64))
}
Expand All @@ -1209,6 +1219,7 @@ func TestUintSliceFlagApply_DefaultValueWithDestination(t *testing.T) {

err := set.Parse([]string{})
assert.NoError(t, err)
assert.NoError(t, fl.PostParse())
assert.Equal(t, defValue, dest)
}

Expand Down Expand Up @@ -1328,6 +1339,7 @@ func TestUint64SliceFlagApply_UsesEnvValues_noDefault(t *testing.T) {

err := set.Parse(nil)
assert.NoError(t, err)
assert.NoError(t, fl.PostParse())
assert.Equal(t, []uint64{1, 2}, set.Lookup("goat").Value.(flag.Getter).Get().([]uint64))
}

Expand All @@ -1341,6 +1353,7 @@ func TestUint64SliceFlagApply_UsesEnvValues_withDefault(t *testing.T) {
_ = fl.Apply(set)
err := set.Parse(nil)
assert.NoError(t, err)
assert.NoError(t, fl.PostParse())
assert.Equal(t, []uint64{1, 2}, set.Lookup("goat").Value.(flag.Getter).Get().([]uint64))
}

Expand All @@ -1354,6 +1367,7 @@ func TestUint64SliceFlagApply_DefaultValueWithDestination(t *testing.T) {

err := set.Parse([]string{})
assert.NoError(t, err)
assert.NoError(t, fl.PostParse())
assert.Equal(t, defValue, dest)
}

Expand Down Expand Up @@ -1519,6 +1533,7 @@ func TestFloat64SliceFlagApply_UsesEnvValues_noDefault(t *testing.T) {

err := set.Parse(nil)
assert.NoError(t, err)
assert.NoError(t, fl.PostParse())
assert.Equal(t, []float64{1, 2}, set.Lookup("goat").Value.(flag.Getter).Get().([]float64))
}

Expand All @@ -1532,6 +1547,7 @@ func TestFloat64SliceFlagApply_UsesEnvValues_withDefault(t *testing.T) {
_ = fl.Apply(set)
err := set.Parse(nil)
assert.NoError(t, err)
assert.NoError(t, fl.PostParse())
assert.Equal(t, []float64{1, 2}, set.Lookup("goat").Value.(flag.Getter).Get().([]float64))
}

Expand Down Expand Up @@ -3056,7 +3072,9 @@ func TestStringMapFlagApply_UsesEnvValues_noDefault(t *testing.T) {
_ = fl.Apply(set)

err := set.Parse(nil)

assert.NoError(t, err)
assert.NoError(t, fl.PostParse())
assert.Nil(t, val)
assert.Equal(t, map[string]string{"vincent van goat": "scape goat"}, set.Lookup("goat").Value.(flag.Getter).Get())
}
Expand All @@ -3071,6 +3089,7 @@ func TestStringMapFlagApply_UsesEnvValues_withDefault(t *testing.T) {
_ = fl.Apply(set)
err := set.Parse(nil)
assert.NoError(t, err)
assert.NoError(t, fl.PostParse())
assert.Equal(t, map[string]string{`some default`: `values here`}, val)
assert.Equal(t, map[string]string{"vincent van goat": "scape goat"}, set.Lookup("goat").Value.(flag.Getter).Get())
}
Expand Down
7 changes: 7 additions & 0 deletions godoc-current.txt
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,8 @@ func (parent *BoolWithInverseFlag) IsSet() bool

func (parent *BoolWithInverseFlag) Names() []string

func (parent *BoolWithInverseFlag) PostParse() error

func (parent *BoolWithInverseFlag) RunAction(ctx context.Context, cmd *Command) error

func (parent *BoolWithInverseFlag) String() string
Expand Down Expand Up @@ -625,6 +627,8 @@ type ExitErrHandlerFunc func(context.Context, *Command, error)
type Flag interface {
fmt.Stringer

PostParse() error

// Apply Flag settings to the given flag set
Apply(*flag.FlagSet) error

Expand Down Expand Up @@ -734,6 +738,9 @@ func (f *FlagBase[T, C, V]) IsVisible() bool
func (f *FlagBase[T, C, V]) Names() []string
Names returns the names of the flag

func (f *FlagBase[T, C, V]) PostParse() error
PostParse populates the flag given the flag set and environment

func (f *FlagBase[T, C, V]) RunAction(ctx context.Context, cmd *Command) error
RunAction executes flag action if set

Expand Down
7 changes: 7 additions & 0 deletions testdata/godoc-v3.x.txt
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,8 @@ func (parent *BoolWithInverseFlag) IsSet() bool

func (parent *BoolWithInverseFlag) Names() []string

func (parent *BoolWithInverseFlag) PostParse() error

func (parent *BoolWithInverseFlag) RunAction(ctx context.Context, cmd *Command) error

func (parent *BoolWithInverseFlag) String() string
Expand Down Expand Up @@ -625,6 +627,8 @@ type ExitErrHandlerFunc func(context.Context, *Command, error)
type Flag interface {
fmt.Stringer

PostParse() error

// Apply Flag settings to the given flag set
Apply(*flag.FlagSet) error

Expand Down Expand Up @@ -734,6 +738,9 @@ func (f *FlagBase[T, C, V]) IsVisible() bool
func (f *FlagBase[T, C, V]) Names() []string
Names returns the names of the flag

func (f *FlagBase[T, C, V]) PostParse() error
PostParse populates the flag given the flag set and environment

func (f *FlagBase[T, C, V]) RunAction(ctx context.Context, cmd *Command) error
RunAction executes flag action if set

Expand Down

0 comments on commit 13b74ee

Please sign in to comment.