Command: Add new field ErrorOnShadowedFlags and error on shadowed flag in ExecuteC

This commit is contained in:
Burak Ok 2022-12-21 17:11:32 +01:00
parent da49908486
commit 185510de5f
2 changed files with 111 additions and 0 deletions

View File

@ -244,6 +244,9 @@ type Command struct {
// SuggestionsMinimumDistance defines minimum levenshtein distance to display suggestions. // SuggestionsMinimumDistance defines minimum levenshtein distance to display suggestions.
// Must be > 0. // Must be > 0.
SuggestionsMinimumDistance int SuggestionsMinimumDistance int
// ErrorOnShadowedFlags will check for any shadowed flags when calling Execute and returns and error
ErrorOnShadowedFlags bool
} }
// Context returns underlying command context. If command was executed // Context returns underlying command context. If command was executed
@ -1065,6 +1068,17 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
cmd.ctx = c.ctx cmd.ctx = c.ctx
} }
if c.ErrorOnShadowedFlags {
clashingFlags := cmd.getClashingFlagnames()
if len(clashingFlags) != 0 {
errorString := "Error: The following flags would shadow each other:\n"
for _, clashingFlag := range clashingFlags {
errorString += fmt.Sprintf(" - Flag '%s' with usage '%s'\n", clashingFlag.Name, clashingFlag.Usage)
}
return cmd, fmt.Errorf(errorString)
}
}
err = cmd.execute(flags) err = cmd.execute(flags)
if err != nil { if err != nil {
// Always show help if requested, even if SilenceErrors is in // Always show help if requested, even if SilenceErrors is in

View File

@ -898,6 +898,103 @@ func TestHelpCommandExecutedOnChild(t *testing.T) {
checkStringContains(t, output, childCmd.Long) checkStringContains(t, output, childCmd.Long)
} }
func TestErrorOnShadowedFlagsOnShadowedFlag(t *testing.T) {
parent := &Command{Use: "parent", Run: emptyRun, ErrorOnShadowedFlags: true}
child := &Command{Use: "child", Run: emptyRun}
parent.AddCommand(child)
parent.PersistentFlags().Bool("foo", false, "parent foo usage")
parent.PersistentFlags().Bool("bar", false, "parent bar usage")
child.Flags().Bool("foo", false, "child foo usage") // This shadows parent's foo flag
child.Flags().Bool("baz", false, "child baz usage")
_, err := executeCommand(parent, "child")
if err == nil {
t.Errorf("Expected an error")
return
}
expectedErrorStrings := []string{
" - Flag 'foo' with usage 'parent foo usage'",
" - Flag 'foo' with usage 'child foo usage'",
}
errorString := err.Error()
for _, expectedErrorString := range expectedErrorStrings {
if !strings.Contains(errorString, expectedErrorString) {
t.Errorf("Expected the string \"%s\" in the error", expectedErrorString)
}
}
}
func TestErrorOnShadowedFlagsOnSelfShadowedFlag(t *testing.T) {
parent := &Command{Use: "parent", Run: emptyRun, ErrorOnShadowedFlags: true}
parent.PersistentFlags().Bool("foo", false, "parent persistent foo usage")
parent.Flags().Bool("foo", false, "parent foo usage")
_, err := executeCommand(parent, "parent")
if err == nil {
t.Errorf("Expected an error")
return
}
expectedErrorStrings := []string{
" - Flag 'foo' with usage 'parent persistent foo usage'",
" - Flag 'foo' with usage 'parent foo usage'",
}
errorString := err.Error()
for _, expectedErrorString := range expectedErrorStrings {
if !strings.Contains(errorString, expectedErrorString) {
t.Errorf("String %q is not present in error: %q", expectedErrorString, err)
}
}
}
func TestErrorOnShadowedFlagsOnNoShadowedFlag(t *testing.T) {
parent := &Command{Use: "parent", Run: emptyRun, ErrorOnShadowedFlags: true}
parent.PersistentFlags().Bool("foo", false, "parent persistent foo usage")
parent.Flags().Bool("bar", false, "parent bar usage")
_, err := executeCommand(parent, "child")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
}
func TestErrorOnShadowedOnShadowedFlagParentAndChild(t *testing.T) {
parent := &Command{Use: "parent", Run: emptyRun, ErrorOnShadowedFlags: true}
child := &Command{Use: "child", Run: emptyRun}
grandchild := &Command{Use: "grandchild", Run: emptyRun}
parent.AddCommand(child)
child.AddCommand(grandchild)
parent.PersistentFlags().Bool("foo", false, "parent foo usage")
parent.PersistentFlags().Bool("bar", false, "parent bar usage")
child.PersistentFlags().Bool("bar", false, "child bar usage")
grandchild.Flags().Bool("baz", false, "grandchild baz usage")
_, err := executeCommand(parent, "child", "grandchild")
if err == nil {
t.Errorf("Expected an error")
return
}
expectedErrorStrings := []string{
" - Flag 'bar' with usage 'parent bar usage'",
" - Flag 'bar' with usage 'child bar usage'",
}
errorString := err.Error()
for _, expectedErrorString := range expectedErrorStrings {
if !strings.Contains(errorString, expectedErrorString) {
t.Errorf("Expected the string \"%s\" in the error", expectedErrorString)
}
}
}
func TestHelpCommandExecutedOnChildWithFlagThatShadowsParentFlag(t *testing.T) { func TestHelpCommandExecutedOnChildWithFlagThatShadowsParentFlag(t *testing.T) {
parent := &Command{Use: "parent", Run: emptyRun} parent := &Command{Use: "parent", Run: emptyRun}
child := &Command{Use: "child", Run: emptyRun} child := &Command{Use: "child", Run: emptyRun}