diff --git a/command.go b/command.go index 10e0a89..97cf024 100644 --- a/command.go +++ b/command.go @@ -1,7 +1,11 @@ package console import ( + "encoding/csv" + "strings" + "github.com/spf13/cobra" + "github.com/spf13/pflag" ) const ( @@ -76,3 +80,40 @@ next: c.filters = updated } + +// resetFlagsDefaults resets all flags on a command to their default values. +func resetFlagsDefaults(target *cobra.Command) { + if target == nil { + return + } + + target.Flags().VisitAll(func(flag *pflag.Flag) { + flag.Changed = false + + switch value := flag.Value.(type) { + case pflag.SliceValue: + _ = value.Replace(parseSliceDefault(flag.DefValue)) + default: + _ = flag.Value.Set(flag.DefValue) + } + }) +} + +func parseSliceDefault(defValue string) []string { + if defValue == "" || defValue == "[]" { + return nil + } + if strings.HasPrefix(defValue, "[") && strings.HasSuffix(defValue, "]") { + defValue = defValue[1 : len(defValue)-1] + } + if defValue == "" { + return nil + } + + values, err := csv.NewReader(strings.NewReader(defValue)).Read() + if err != nil { + return []string{defValue} + } + + return values +} diff --git a/run.go b/run.go index 8cd2653..2b0c7b4 100644 --- a/run.go +++ b/run.go @@ -166,6 +166,8 @@ func (c *Console) execute(ctx context.Context, menu *Menu, args []string, async return err } + resetFlagsDefaults(target) + // Console-wide pre-run hooks, cannot. if err := c.runAllE(c.PreCmdRunHooks); err != nil { return fmt.Errorf("pre-run error: %s", err.Error()) diff --git a/run_flags_test.go b/run_flags_test.go new file mode 100644 index 0000000..82679c7 --- /dev/null +++ b/run_flags_test.go @@ -0,0 +1,73 @@ +package console + +import ( + "context" + "reflect" + "testing" + + "github.com/spf13/cobra" +) + +func TestRunCommandArgsResetsFlagDefaults(t *testing.T) { + c := New("test") + menu := c.ActiveMenu() + + type runState struct { + verbose bool + verboseChanged bool + items []string + itemsChanged bool + } + var states []runState + + root := &cobra.Command{Use: "root"} + cmd := &cobra.Command{ + Use: "run", + RunE: func(cmd *cobra.Command, _ []string) error { + verbose, err := cmd.Flags().GetBool("verbose") + if err != nil { + return err + } + items, err := cmd.Flags().GetStringSlice("item") + if err != nil { + return err + } + + states = append(states, runState{ + verbose: verbose, + verboseChanged: cmd.Flags().Changed("verbose"), + items: append([]string(nil), items...), + itemsChanged: cmd.Flags().Changed("item"), + }) + + return nil + }, + } + cmd.Flags().Bool("verbose", false, "") + cmd.Flags().StringSlice("item", []string{"base"}, "") + root.AddCommand(cmd) + menu.SetCommands(func() *cobra.Command { return root }) + + if err := menu.RunCommandArgs(context.Background(), []string{"run", "--verbose", "--item", "one", "--item", "two"}); err != nil { + t.Fatal(err) + } + if err := menu.RunCommandArgs(context.Background(), []string{"run"}); err != nil { + t.Fatal(err) + } + + if len(states) != 2 { + t.Fatalf("executed %d times, want 2", len(states)) + } + if !states[0].verbose || !states[0].verboseChanged { + t.Fatalf("first run verbose state = %+v, want true/changed", states[0]) + } + if !reflect.DeepEqual(states[0].items, []string{"one", "two"}) || !states[0].itemsChanged { + t.Fatalf("first run slice state = %+v, want [one two]/changed", states[0]) + } + if states[1].verbose || states[1].verboseChanged { + t.Fatalf("second run verbose state = %+v, want false/not changed", states[1]) + } + if !reflect.DeepEqual(states[1].items, []string{"base"}) || states[1].itemsChanged { + t.Fatalf("second run slice state = %+v, want [base]/not changed", states[1]) + } +}