-
Notifications
You must be signed in to change notification settings - Fork 42
Adding displaying of flags when using unknown flags for a command #239
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
317c79c
3682bcf
0ba0f86
4f5cd67
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,6 +24,10 @@ func NewCommandFlagsParser(flag *flag.FlagSet, parser FlagsParser, validator Fla | |
|
|
||
| // Parse parsing the args | ||
| func (p *CommandFlagsParser) Parse(args []string) error { | ||
| if unknownFlags := collectUnknownFlags(p.flag, args); len(unknownFlags) > 0 { | ||
| return fmt.Errorf("Unknown or wrong flags: %s", strings.Join(unknownFlags, ", ")) | ||
| } | ||
|
|
||
|
||
| err := p.parser.ParseFlags(p.flag, args) | ||
| if err != nil { | ||
| return err | ||
|
|
@@ -88,7 +92,7 @@ func (p DefaultCommandFlagsParser) ParseFlags(flags *flag.FlagSet, args []string | |
| // Parse the arguments | ||
| err := flags.Parse(args[positionalArgsCount:]) | ||
| if err != nil { | ||
| return errors.New("Unknown or wrong flag") | ||
| return errors.New("Parsing of arguments has failed") | ||
| } | ||
|
|
||
| // Check for wrong arguments | ||
|
|
@@ -124,3 +128,67 @@ func (v DefaultCommandFlagsValidator) ValidateParsedFlags(flags *flag.FlagSet) e | |
|
|
||
| return nil | ||
| } | ||
|
|
||
| func collectUnknownFlags(flags *flag.FlagSet, args []string) []string { | ||
| var unknownFlags []string | ||
| alreadySeenUnknownFLags := make(map[string]int) | ||
|
|
||
| for i := 0; i < len(args); i++ { | ||
| currentArgument := args[i] | ||
|
|
||
| if !strings.HasPrefix(currentArgument, "-") { | ||
| continue | ||
| } | ||
|
|
||
| currentFlag := currentArgument | ||
| flagName := strings.TrimLeft(currentFlag, "-") | ||
|
|
||
| if flagName == "" { | ||
| continue | ||
| } | ||
|
|
||
| isFlagKnown := flags.Lookup(flagName) | ||
| if isFlagKnown != nil { | ||
| isBoolean := isBoolFlag(isFlagKnown) | ||
| if !isBoolean { | ||
| i = tryToGetNext(args, i) | ||
| } | ||
| continue | ||
| } | ||
|
|
||
| appendOnlyWhenCountIsOne(alreadySeenUnknownFLags, currentFlag, &unknownFlags) | ||
| i = tryToGetNext(args, i) | ||
| } | ||
|
|
||
| return unknownFlags | ||
| } | ||
|
|
||
| func isBoolFlag(flag *flag.Flag) bool { | ||
| type boolFlagInterface interface{ IsBoolFlag() bool } | ||
|
|
||
| boolFlag, isInterfaceImplemented := flag.Value.(boolFlagInterface) | ||
| if !isInterfaceImplemented { | ||
| return false | ||
| } | ||
|
|
||
| return boolFlag.IsBoolFlag() | ||
| } | ||
|
|
||
| func tryToGetNext(args []string, currentIndex int) int { | ||
| nextIndex := currentIndex + 1 | ||
| if nextIndex < len(args) { | ||
| nextArgument := args[nextIndex] | ||
| nextHasPrefixDash := strings.HasPrefix(nextArgument, "-") | ||
| if !nextHasPrefixDash { | ||
| return nextIndex | ||
| } | ||
| } | ||
| return currentIndex | ||
| } | ||
|
|
||
| func appendOnlyWhenCountIsOne(alreadySeenUnknownFLags map[string]int, currentFlag string, unknownFlags *[]string) { | ||
| alreadySeenUnknownFLags[currentFlag]++ | ||
| if alreadySeenUnknownFLags[currentFlag] == 1 { | ||
| *unknownFlags = append(*unknownFlags, currentFlag) | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this check going to be executed on each parse? Why not execute it only when the parsing fails?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, the check is going to be executed for each new command's arguments. I thought it was best to have this check for unknown flags as early as possible, because I think its rather a big mistake having unsupported flags and if there are any - then it is better to directly exit with an error and not even go through the execution of the internal ParseFlags() method and after it is finished to make the check for unknown flags in the block after thats supposed to take care of errors returned by ParseFlags() method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This approach is cleaner code-wise since its clearly separated and fails earlier. Still, if we assume generally that errors with flags are more rare, I think that a check only when the
flags.Parse()fails would be preferred.Also there is the added risk of false-positives when checking before
flags.Parse(), since were essentially adding more restrictions to the command execution with the new logic.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed!