Merge pull request #691 from urfave/refactor-686
Refactor flag handling logic
This commit is contained in:
commit
39908eb08f
140
command.go
140
command.go
@ -1,6 +1,7 @@
|
|||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"sort"
|
"sort"
|
||||||
@ -110,43 +111,7 @@ func (c Command) Run(ctx *Context) (err error) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
set, err := flagSet(c.Name, c.Flags)
|
set, err := c.parseFlags(ctx.Args().Tail())
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
set.SetOutput(ioutil.Discard)
|
|
||||||
firstFlagIndex, terminatorIndex := getIndexes(ctx)
|
|
||||||
flagArgs, regularArgs := getAllArgs(ctx.Args(), firstFlagIndex, terminatorIndex)
|
|
||||||
if c.UseShortOptionHandling {
|
|
||||||
flagArgs = translateShortOptions(flagArgs)
|
|
||||||
}
|
|
||||||
if c.SkipFlagParsing {
|
|
||||||
err = set.Parse(append([]string{"--"}, ctx.Args().Tail()...))
|
|
||||||
} else if !c.SkipArgReorder {
|
|
||||||
if firstFlagIndex > -1 {
|
|
||||||
err = set.Parse(append(flagArgs, regularArgs...))
|
|
||||||
} else {
|
|
||||||
err = set.Parse(ctx.Args().Tail())
|
|
||||||
}
|
|
||||||
} else if c.UseShortOptionHandling {
|
|
||||||
if terminatorIndex == -1 && firstFlagIndex > -1 {
|
|
||||||
// Handle shortname AND no options
|
|
||||||
err = set.Parse(append(regularArgs, flagArgs...))
|
|
||||||
} else {
|
|
||||||
// Handle shortname and options
|
|
||||||
err = set.Parse(flagArgs)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = set.Parse(append(regularArgs, flagArgs...))
|
|
||||||
}
|
|
||||||
|
|
||||||
nerr := normalizeFlags(c.Flags, set)
|
|
||||||
if nerr != nil {
|
|
||||||
fmt.Fprintln(ctx.App.Writer, nerr)
|
|
||||||
fmt.Fprintln(ctx.App.Writer)
|
|
||||||
ShowCommandHelp(ctx, c.Name)
|
|
||||||
return nerr
|
|
||||||
}
|
|
||||||
|
|
||||||
context := NewContext(ctx.App, set, ctx)
|
context := NewContext(ctx.App, set, ctx)
|
||||||
context.Command = c
|
context.Command = c
|
||||||
@ -205,60 +170,65 @@ func (c Command) Run(ctx *Context) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func getIndexes(ctx *Context) (int, int) {
|
func (c *Command) parseFlags(args Args) (*flag.FlagSet, error) {
|
||||||
firstFlagIndex := -1
|
set, err := flagSet(c.Name, c.Flags)
|
||||||
terminatorIndex := -1
|
if err != nil {
|
||||||
for index, arg := range ctx.Args() {
|
return nil, err
|
||||||
|
}
|
||||||
|
set.SetOutput(ioutil.Discard)
|
||||||
|
|
||||||
|
if c.SkipFlagParsing {
|
||||||
|
return set, set.Parse(append([]string{c.Name, "--"}, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.UseShortOptionHandling {
|
||||||
|
args = translateShortOptions(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.SkipArgReorder {
|
||||||
|
args = reorderArgs(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = set.Parse(args)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = normalizeFlags(c.Flags, set)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return set, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// reorderArgs moves all flags before arguments as this is what flag expects
|
||||||
|
func reorderArgs(args []string) []string {
|
||||||
|
var nonflags, flags []string
|
||||||
|
|
||||||
|
readFlagValue := false
|
||||||
|
for i, arg := range args {
|
||||||
if arg == "--" {
|
if arg == "--" {
|
||||||
terminatorIndex = index
|
nonflags = append(nonflags, args[i:]...)
|
||||||
break
|
break
|
||||||
} else if arg == "-" {
|
}
|
||||||
// Do nothing. A dash alone is not really a flag.
|
|
||||||
|
if readFlagValue {
|
||||||
|
readFlagValue = false
|
||||||
|
flags = append(flags, arg)
|
||||||
continue
|
continue
|
||||||
} else if strings.HasPrefix(arg, "-") && firstFlagIndex == -1 {
|
}
|
||||||
firstFlagIndex = index
|
|
||||||
|
if arg != "-" && strings.HasPrefix(arg, "-") {
|
||||||
|
flags = append(flags, arg)
|
||||||
|
|
||||||
|
readFlagValue = !strings.Contains(arg, "=")
|
||||||
|
} else {
|
||||||
|
nonflags = append(nonflags, arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(ctx.Args()) > 0 && !strings.HasPrefix(ctx.Args()[0], "-") && firstFlagIndex == -1 {
|
|
||||||
return -1, -1
|
|
||||||
}
|
|
||||||
|
|
||||||
return firstFlagIndex, terminatorIndex
|
return append(flags, nonflags...)
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// copyStringslice takes a string slice and copies it
|
|
||||||
func copyStringSlice(slice []string, start, end int) []string {
|
|
||||||
newSlice := make([]string, end-start)
|
|
||||||
copy(newSlice, slice[start:end])
|
|
||||||
return newSlice
|
|
||||||
}
|
|
||||||
|
|
||||||
// getAllArgs extracts and returns two string slices representing
|
|
||||||
// regularArgs and flagArgs
|
|
||||||
func getAllArgs(args []string, firstFlagIndex, terminatorIndex int) ([]string, []string) {
|
|
||||||
var regularArgs []string
|
|
||||||
// if there are no options, the we set the index to 1 manually
|
|
||||||
if firstFlagIndex == -1 {
|
|
||||||
firstFlagIndex = 1
|
|
||||||
regularArgs = copyStringSlice(args, 0, len(args))
|
|
||||||
} else {
|
|
||||||
regularArgs = copyStringSlice(args, 1, firstFlagIndex)
|
|
||||||
}
|
|
||||||
var flagArgs []string
|
|
||||||
// a flag terminatorIndex was found in the input. we need to collect
|
|
||||||
// flagArgs based on it.
|
|
||||||
if terminatorIndex > -1 {
|
|
||||||
flagArgs = copyStringSlice(args, firstFlagIndex, terminatorIndex)
|
|
||||||
additionalRegularArgs := copyStringSlice(args, terminatorIndex, len(args))
|
|
||||||
regularArgs = append(regularArgs, additionalRegularArgs...)
|
|
||||||
for _, i := range additionalRegularArgs {
|
|
||||||
regularArgs = append(regularArgs, i)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
flagArgs = args[firstFlagIndex:]
|
|
||||||
}
|
|
||||||
return flagArgs, regularArgs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func translateShortOptions(flagArgs Args) []string {
|
func translateShortOptions(flagArgs Args) []string {
|
||||||
|
@ -243,3 +243,41 @@ func TestCommand_Run_SubcommandsCanUseErrWriter(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCommandFlagReordering(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
testArgs []string
|
||||||
|
expectedValue string
|
||||||
|
expectedArgs []string
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
{[]string{"some-exec", "some-command", "some-arg", "--flag", "foo"}, "foo", []string{"some-arg"}, nil},
|
||||||
|
{[]string{"some-exec", "some-command", "some-arg", "--flag=foo"}, "foo", []string{"some-arg"}, nil},
|
||||||
|
{[]string{"some-exec", "some-command", "--flag=foo", "some-arg"}, "foo", []string{"some-arg"}, nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
value := ""
|
||||||
|
args := []string{}
|
||||||
|
app := &App{
|
||||||
|
Commands: []Command{
|
||||||
|
{
|
||||||
|
Name: "some-command",
|
||||||
|
Flags: []Flag{
|
||||||
|
StringFlag{Name: "flag"},
|
||||||
|
},
|
||||||
|
Action: func(c *Context) {
|
||||||
|
fmt.Printf("%+v\n", c.String("flag"))
|
||||||
|
value = c.String("flag")
|
||||||
|
args = c.Args()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := app.Run(c.testArgs)
|
||||||
|
expect(t, err, c.expectedErr)
|
||||||
|
expect(t, value, c.expectedValue)
|
||||||
|
expect(t, args, c.expectedArgs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user