mirror of
https://github.com/spf13/cobra
synced 2024-11-24 22:57:12 +00:00
parent
78bfc837fe
commit
8ba575e694
2 changed files with 111 additions and 17 deletions
68
command.go
68
command.go
|
@ -643,13 +643,14 @@ func shortHasNoOptDefVal(name string, fs *flag.FlagSet) bool {
|
||||||
return flag.NoOptDefVal != ""
|
return flag.NoOptDefVal != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func stripFlags(args []string, c *Command) []string {
|
func stripFlags(args []string, c *Command) ([]string, []string) {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
return args
|
return args, nil
|
||||||
}
|
}
|
||||||
c.mergePersistentFlags()
|
c.mergePersistentFlags()
|
||||||
|
|
||||||
commands := []string{}
|
commands := []string{}
|
||||||
|
flagsThatConsumeNextArg := []string{} // We use this to avoid repeating the same lengthy logic for parsing shorthand combinations in argsMinusFirstX
|
||||||
flags := c.Flags()
|
flags := c.Flags()
|
||||||
|
|
||||||
Loop:
|
Loop:
|
||||||
|
@ -665,31 +666,70 @@ Loop:
|
||||||
// delete arg from args.
|
// delete arg from args.
|
||||||
fallthrough // (do the same as below)
|
fallthrough // (do the same as below)
|
||||||
case strings.HasPrefix(s, "-") && !strings.Contains(s, "=") && len(s) == 2 && !shortHasNoOptDefVal(s[1:], flags):
|
case strings.HasPrefix(s, "-") && !strings.Contains(s, "=") && len(s) == 2 && !shortHasNoOptDefVal(s[1:], flags):
|
||||||
|
flagsThatConsumeNextArg = append(flagsThatConsumeNextArg, s)
|
||||||
// If '-f arg' then
|
// If '-f arg' then
|
||||||
// delete 'arg' from args or break the loop if len(args) <= 1.
|
// delete 'arg' from args or break the loop if len(args) <= 1.
|
||||||
if len(args) <= 1 {
|
if len(args) <= 1 {
|
||||||
break Loop
|
break Loop
|
||||||
} else {
|
} else {
|
||||||
args = args[1:]
|
args = args[1:]
|
||||||
continue
|
}
|
||||||
|
case strings.HasPrefix(s, "-") && !strings.HasPrefix(s, "--") && !strings.Contains(s, "=") && len(s) > 2:
|
||||||
|
shorthandCombination := s[1:] // Skip the leading "-"
|
||||||
|
lastPos := len(shorthandCombination) - 1
|
||||||
|
for i, shorthand := range shorthandCombination {
|
||||||
|
if shortHasNoOptDefVal(string(shorthand), flags) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// We found a shorthand that needs a value.
|
||||||
|
|
||||||
|
if i == lastPos {
|
||||||
|
// Since we're at the end of the shorthand combination, this means that the
|
||||||
|
// value for the shorthand is given in the next argument. (e.g. '-xyzf arg',
|
||||||
|
// where -x, -y, -z are boolean flags, and -f is a flag that needs a value).
|
||||||
|
|
||||||
|
// The whole combination will take a value.
|
||||||
|
flagsThatConsumeNextArg = append(flagsThatConsumeNextArg, s)
|
||||||
|
|
||||||
|
if len(args) <= 1 {
|
||||||
|
break Loop
|
||||||
|
} else {
|
||||||
|
args = args[1:]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Since the shorthand combination doesn't end here, this means that the
|
||||||
|
// value for the shorthand is given in the same argument, meaning we don't
|
||||||
|
// have to consume the next one. (e.g. '-xyzfarg', where -x, -y, -z are
|
||||||
|
// boolean flags, and -f is a flag that needs a value).
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
}
|
}
|
||||||
case s != "" && !strings.HasPrefix(s, "-"):
|
case s != "" && !strings.HasPrefix(s, "-"):
|
||||||
commands = append(commands, s)
|
commands = append(commands, s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return commands
|
return commands, flagsThatConsumeNextArg
|
||||||
}
|
}
|
||||||
|
|
||||||
// argsMinusFirstX removes only the first x from args. Otherwise, commands that look like
|
// argsMinusFirstX removes only the first x from args. Otherwise, commands that look like
|
||||||
// openshift admin policy add-role-to-user admin my-user, lose the admin argument (arg[4]).
|
// openshift admin policy add-role-to-user admin my-user, lose the admin argument (arg[4]).
|
||||||
// Special care needs to be taken not to remove a flag value.
|
// Special care needs to be taken not to remove a flag value.
|
||||||
func (c *Command) argsMinusFirstX(args []string, x string) []string {
|
func (c *Command) argsMinusFirstX(args, flagsThatConsumeNextArg []string, x string) []string {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
c.mergePersistentFlags()
|
|
||||||
flags := c.Flags()
|
consumesNextArg := func(flag string) bool {
|
||||||
|
for _, f := range flagsThatConsumeNextArg {
|
||||||
|
if flag == f {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
Loop:
|
Loop:
|
||||||
for pos := 0; pos < len(args); pos++ {
|
for pos := 0; pos < len(args); pos++ {
|
||||||
|
@ -698,13 +738,8 @@ Loop:
|
||||||
case s == "--":
|
case s == "--":
|
||||||
// -- means we have reached the end of the parseable args. Break out of the loop now.
|
// -- means we have reached the end of the parseable args. Break out of the loop now.
|
||||||
break Loop
|
break Loop
|
||||||
case strings.HasPrefix(s, "--") && !strings.Contains(s, "=") && !hasNoOptDefVal(s[2:], flags):
|
case consumesNextArg(s):
|
||||||
fallthrough
|
|
||||||
case strings.HasPrefix(s, "-") && !strings.Contains(s, "=") && len(s) == 2 && !shortHasNoOptDefVal(s[1:], flags):
|
|
||||||
// This is a flag without a default value, and an equal sign is not used. Increment pos in order to skip
|
|
||||||
// over the next arg, because that is the value of this flag.
|
|
||||||
pos++
|
pos++
|
||||||
continue
|
|
||||||
case !strings.HasPrefix(s, "-"):
|
case !strings.HasPrefix(s, "-"):
|
||||||
// This is not a flag or a flag value. Check to see if it matches what we're looking for, and if so,
|
// This is not a flag or a flag value. Check to see if it matches what we're looking for, and if so,
|
||||||
// return the args, excluding the one at this position.
|
// return the args, excluding the one at this position.
|
||||||
|
@ -730,7 +765,7 @@ func (c *Command) Find(args []string) (*Command, []string, error) {
|
||||||
var innerfind func(*Command, []string) (*Command, []string)
|
var innerfind func(*Command, []string) (*Command, []string)
|
||||||
|
|
||||||
innerfind = func(c *Command, innerArgs []string) (*Command, []string) {
|
innerfind = func(c *Command, innerArgs []string) (*Command, []string) {
|
||||||
argsWOflags := stripFlags(innerArgs, c)
|
argsWOflags, flagsThatConsumeNextArg := stripFlags(innerArgs, c)
|
||||||
if len(argsWOflags) == 0 {
|
if len(argsWOflags) == 0 {
|
||||||
return c, innerArgs
|
return c, innerArgs
|
||||||
}
|
}
|
||||||
|
@ -738,14 +773,15 @@ func (c *Command) Find(args []string) (*Command, []string, error) {
|
||||||
|
|
||||||
cmd := c.findNext(nextSubCmd)
|
cmd := c.findNext(nextSubCmd)
|
||||||
if cmd != nil {
|
if cmd != nil {
|
||||||
return innerfind(cmd, c.argsMinusFirstX(innerArgs, nextSubCmd))
|
return innerfind(cmd, c.argsMinusFirstX(innerArgs, flagsThatConsumeNextArg, nextSubCmd))
|
||||||
}
|
}
|
||||||
return c, innerArgs
|
return c, innerArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
commandFound, a := innerfind(c, args)
|
commandFound, a := innerfind(c, args)
|
||||||
if commandFound.Args == nil {
|
if commandFound.Args == nil {
|
||||||
return commandFound, a, legacyArgs(commandFound, stripFlags(a, commandFound))
|
argsWOflags, _ := stripFlags(a, commandFound)
|
||||||
|
return commandFound, a, legacyArgs(commandFound, argsWOflags)
|
||||||
}
|
}
|
||||||
return commandFound, a, nil
|
return commandFound, a, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -693,6 +693,30 @@ func TestStripFlags(t *testing.T) {
|
||||||
[]string{"-p", "bar"},
|
[]string{"-p", "bar"},
|
||||||
[]string{"bar"},
|
[]string{"bar"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
[]string{"-s", "value", "bar"},
|
||||||
|
[]string{"bar"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"-s=value", "bar"},
|
||||||
|
[]string{"bar"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"-svalue", "bar"},
|
||||||
|
[]string{"bar"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"-ps", "value", "bar"},
|
||||||
|
[]string{"bar"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"-ps=value", "bar"},
|
||||||
|
[]string{"bar"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"-psvalue", "bar"},
|
||||||
|
[]string{"bar"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
c := &Command{Use: "c", Run: emptyRun}
|
c := &Command{Use: "c", Run: emptyRun}
|
||||||
|
@ -702,7 +726,7 @@ func TestStripFlags(t *testing.T) {
|
||||||
c.Flags().BoolP("bool", "b", false, "")
|
c.Flags().BoolP("bool", "b", false, "")
|
||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
got := stripFlags(test.input, c)
|
got, _ := stripFlags(test.input, c)
|
||||||
if !reflect.DeepEqual(test.output, got) {
|
if !reflect.DeepEqual(test.output, got) {
|
||||||
t.Errorf("(%v) Expected: %v, got: %v", i, test.output, got)
|
t.Errorf("(%v) Expected: %v, got: %v", i, test.output, got)
|
||||||
}
|
}
|
||||||
|
@ -2688,11 +2712,13 @@ func TestHelpflagCommandExecutedWithoutVersionSet(t *testing.T) {
|
||||||
|
|
||||||
func TestFind(t *testing.T) {
|
func TestFind(t *testing.T) {
|
||||||
var foo, bar string
|
var foo, bar string
|
||||||
|
var persist bool
|
||||||
root := &Command{
|
root := &Command{
|
||||||
Use: "root",
|
Use: "root",
|
||||||
}
|
}
|
||||||
root.PersistentFlags().StringVarP(&foo, "foo", "f", "", "")
|
root.PersistentFlags().StringVarP(&foo, "foo", "f", "", "")
|
||||||
root.PersistentFlags().StringVarP(&bar, "bar", "b", "something", "")
|
root.PersistentFlags().StringVarP(&bar, "bar", "b", "something", "")
|
||||||
|
root.PersistentFlags().BoolVarP(&persist, "persist", "p", false, "")
|
||||||
|
|
||||||
child := &Command{
|
child := &Command{
|
||||||
Use: "child",
|
Use: "child",
|
||||||
|
@ -2755,6 +2781,38 @@ func TestFind(t *testing.T) {
|
||||||
[]string{"--foo", "child", "--bar", "something", "child"},
|
[]string{"--foo", "child", "--bar", "something", "child"},
|
||||||
[]string{"--foo", "child", "--bar", "something"},
|
[]string{"--foo", "child", "--bar", "something"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
[]string{"-f", "value", "child"},
|
||||||
|
[]string{"-f", "value"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"-f=value", "child"},
|
||||||
|
[]string{"-f=value"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"-fvalue", "child"},
|
||||||
|
[]string{"-fvalue"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"-pf", "value", "child"},
|
||||||
|
[]string{"-pf", "value"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"-pf=value", "child"},
|
||||||
|
[]string{"-pf=value"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"-pfvalue", "child"},
|
||||||
|
[]string{"-pfvalue"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"-pf", "child", "child"},
|
||||||
|
[]string{"-pf", "child"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"-pf", "child", "-pb", "something", "child"},
|
||||||
|
[]string{"-pf", "child", "-pb", "something"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
|
|
Loading…
Reference in a new issue