Retain entered flag spellings in parser failures
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
package cobra
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
commandmeta "gitea.maximumdirect.net/eric/CommandKit/internal/commandmeta"
|
||||
)
|
||||
|
||||
func interpretFlagTokens(argv []string, flags []commandmeta.Flag, aliases []commandmeta.FlagAlias) ([]string, map[string][]string) {
|
||||
func interpretFlagTokens(argv []string, flags []commandmeta.Flag, aliases []commandmeta.FlagAlias) ([]string, map[string][]string, *commandmeta.Failure) {
|
||||
canonical := make(map[string]commandmeta.Flag, len(flags)*2+2)
|
||||
help := commandmeta.Flag{Name: "help", Short: "h", Boolean: true}
|
||||
canonical["--help"] = help
|
||||
@@ -61,6 +62,9 @@ func interpretFlagTokens(argv []string, flags []commandmeta.Flag, aliases []comm
|
||||
index += 2
|
||||
continue
|
||||
}
|
||||
if !flag.Boolean {
|
||||
return interpreted, spellings, &commandmeta.Failure{Message: fmt.Sprintf("flag needs an argument: %s", original)}
|
||||
}
|
||||
interpreted = append(interpreted, "--"+flag.Name)
|
||||
index++
|
||||
continue
|
||||
@@ -107,8 +111,7 @@ func interpretFlagTokens(argv []string, flags []commandmeta.Flag, aliases []comm
|
||||
consumedNext = true
|
||||
break
|
||||
}
|
||||
interpreted = append(interpreted, "--"+flag.Name)
|
||||
break
|
||||
return interpreted, spellings, &commandmeta.Failure{Message: fmt.Sprintf("flag needs an argument: %s", original)}
|
||||
}
|
||||
if !consumedNext {
|
||||
index++
|
||||
@@ -118,5 +121,5 @@ func interpretFlagTokens(argv []string, flags []commandmeta.Flag, aliases []comm
|
||||
interpreted = append(interpreted, token)
|
||||
index++
|
||||
}
|
||||
return interpreted, spellings
|
||||
return interpreted, spellings, nil
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package cobra
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
commandmeta "gitea.maximumdirect.net/eric/CommandKit/internal/commandmeta"
|
||||
@@ -158,3 +159,40 @@ func TestParseFlagAliasOccurrenceOrderAndSpellings(t *testing.T) {
|
||||
t.Fatalf("tag spellings = %#v, want %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseFailuresRetainEnteredFlagSpellings(t *testing.T) {
|
||||
for _, spelling := range []string{"--color", "--colour", "-c", "-C"} {
|
||||
t.Run("missing/"+spelling, func(t *testing.T) {
|
||||
result := Parse(flagGrammarTree(), []string{"run", spelling}, flagGrammarOptions())
|
||||
if result.Failure == nil || !strings.Contains(result.Failure.Message, ": "+spelling) {
|
||||
t.Fatalf("missing value failure = %#v, want entered spelling %q", result.Failure, spelling)
|
||||
}
|
||||
})
|
||||
}
|
||||
result := Parse(flagGrammarTree(), []string{"run", "-wC"}, flagGrammarOptions())
|
||||
if result.Failure == nil || !strings.Contains(result.Failure.Message, ": -C") {
|
||||
t.Fatalf("cluster missing value failure = %#v", result.Failure)
|
||||
}
|
||||
for _, test := range []struct {
|
||||
args []string
|
||||
spelling string
|
||||
}{
|
||||
{[]string{"--hlep=bad"}, "--hlep"},
|
||||
{[]string{"-h=bad"}, "-h"},
|
||||
{[]string{"--help=bad"}, "--help"},
|
||||
{[]string{"--hlep=bad", "--help=true"}, "--hlep"},
|
||||
{[]string{"-h=bad", "--hlep=true"}, "-h"},
|
||||
{[]string{"--help=true", "--hlep=bad"}, "--hlep"},
|
||||
} {
|
||||
t.Run(strings.Join(test.args, " "), func(t *testing.T) {
|
||||
result := Parse(flagGrammarTree(), append([]string{"run"}, test.args...), flagGrammarOptions())
|
||||
if result.Failure == nil || !strings.Contains(result.Failure.Message, "for "+test.spelling) {
|
||||
t.Fatalf("help failure = %#v, want entered spelling %q", result.Failure, test.spelling)
|
||||
}
|
||||
})
|
||||
}
|
||||
result = Parse(flagGrammarTree(), []string{"run", "--unknown", "--colour"}, flagGrammarOptions())
|
||||
if result.Failure == nil || !strings.Contains(result.Failure.Message, "unknown") || strings.Contains(result.Failure.Message, "needs an argument") {
|
||||
t.Fatalf("earlier parser failure = %#v", result.Failure)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ func (session *Session) Parse(argv []string) commandmeta.Result {
|
||||
}
|
||||
|
||||
remaining := append(commandpath.CloneTokens(unconsumed), tail...)
|
||||
interpreted, spellings := interpretFlagTokens(remaining, selectedNode.Flags, session.flagAliases)
|
||||
interpreted, spellings, interpretationFailure := interpretFlagTokens(remaining, selectedNode.Flags, session.flagAliases)
|
||||
for _, flag := range selectedNode.Flags {
|
||||
if value := selected.Flags().Lookup(flag.Name); value != nil {
|
||||
value.Value.(*rawValue).queueSpellings(spellings[flag.Name])
|
||||
@@ -89,6 +89,12 @@ func (session *Session) Parse(argv []string) commandmeta.Result {
|
||||
if err := selected.ParseFlags(interpreted); err != nil {
|
||||
return failure(result, err.Error())
|
||||
}
|
||||
// Parse the completed prefix first so an earlier backend failure keeps its
|
||||
// precedence over a missing value discovered while interpreting aliases.
|
||||
if interpretationFailure != nil {
|
||||
result.Failure = interpretationFailure
|
||||
return result
|
||||
}
|
||||
operands := selected.Flags().Args()
|
||||
|
||||
result.Operands = append([]string(nil), operands...)
|
||||
@@ -473,10 +479,14 @@ func (v *rawValue) spellingsCopy() []string { return append([]string(nil), v.spe
|
||||
|
||||
func (v *rawValue) lastBool() (bool, error) {
|
||||
var value bool
|
||||
for _, raw := range v.values {
|
||||
for index, raw := range v.values {
|
||||
parsed, err := strconv.ParseBool(raw)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("invalid boolean value %q for --help", raw)
|
||||
spelling := "--help"
|
||||
if index < len(v.spellings) {
|
||||
spelling = v.spellings[index]
|
||||
}
|
||||
return false, fmt.Errorf("invalid boolean value %q for %s", raw, spelling)
|
||||
}
|
||||
value = parsed
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user