Переглянути джерело

Update cobra vendor

- adds support for usage strings on flag errors
- adds support for arg validation

Signed-off-by: Daniel Nephin <dnephin@docker.com>
Daniel Nephin 9 роки тому
батько
коміт
3d624ed5d6

+ 1 - 1
hack/vendor.sh

@@ -135,7 +135,7 @@ clone git google.golang.org/cloud dae7e3d993bc3812a2185af60552bb6b847e52a0 https
 clone git github.com/docker/containerd 57b7c3da915ebe943bd304c00890959b191e5264
 
 # cli
-clone git github.com/spf13/cobra 0f866a6211e33cde2091d9290c08f6afd6c9ebbc
+clone git github.com/spf13/cobra acf60156558542e78c6f3695f74b0f871614ff55 https://github.com/dnephin/cobra.git
 clone git github.com/spf13/pflag cb88ea77998c3f024757528e3305022ab50b43be
 clone git github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
 

+ 36 - 0
vendor/src/github.com/spf13/cobra/README.md

@@ -406,6 +406,42 @@ A flag can also be assigned locally which will only apply to that specific comma
 RootCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from")
 ```
 
+### Positional Arguments
+
+Validation of positional arguments can be specified using the `Args` field, which accepts
+one of the following values:
+
+- `NoArgs` - the command will report an error if there are any positional args.
+- `ArbitraryArgs` - the command will accept any args.
+- `OnlyValidArgs` - the command will report an error if there are any positiona 
+  args that are not in the `ValidArgs` list.
+- `MinimumNArgs(int)` - the command will report an error if there are not at 
+  least N positional args.
+- `MaximumNArgs(int)` - the command will report an error if there are more than 
+  N positional args.
+- `ExactArgs(int)` - the command will report an error if there are not 
+  exactly N positional args.
+- `RangeArgs(min, max)` - the command will report an error if the number of args 
+  is not between the minimum and maximum number of expected args.
+
+By default, `Args` uses the following legacy behaviour:
+- root commands with no subcommands can take arbitrary arguments
+- root commands with subcommands will do subcommand validity checking
+- subcommands will always accept arbitrary arguments and do no subsubcommand validity checking
+
+
+```go
+var HugoCmd = &cobra.Command{
+    Use:   "hugo",
+    Short: "Hugo is a very fast static site generator",
+    ValidArgs: []string{"one", "two"}
+    Args: cobra.OnlyValidArgs
+    Run: func(cmd *cobra.Command, args []string) {
+        // args will only have the values one, two
+        // or the cmd.Execute() will fail.
+    },
+}
+```
 
 ## Example
 

+ 98 - 0
vendor/src/github.com/spf13/cobra/args.go

@@ -0,0 +1,98 @@
+package cobra
+
+import (
+	"fmt"
+)
+
+type PositionalArgs func(cmd *Command, args []string) error
+
+// Legacy arg validation has the following behaviour:
+// - root commands with no subcommands can take arbitrary arguments
+// - root commands with subcommands will do subcommand validity checking
+// - subcommands will always accept arbitrary arguments
+func legacyArgs(cmd *Command, args []string) error {
+	// no subcommand, always take args
+	if !cmd.HasSubCommands() {
+		return nil
+	}
+
+	// root command with subcommands, do subcommand checking
+	if !cmd.HasParent() && len(args) > 0 {
+		return fmt.Errorf("unknown command %q for %q%s", args[0], cmd.CommandPath(), cmd.findSuggestions(args[0]))
+	}
+	return nil
+}
+
+// NoArgs returns an error if any args are included
+func NoArgs(cmd *Command, args []string) error {
+	if len(args) > 0 {
+		return fmt.Errorf("unknown command %q for %q", args[0], cmd.CommandPath())
+	}
+	return nil
+}
+
+// OnlyValidArgs returns an error if any args are not in the list of ValidArgs
+func OnlyValidArgs(cmd *Command, args []string) error {
+	if len(cmd.ValidArgs) > 0 {
+		for _, v := range args {
+			if !stringInSlice(v, cmd.ValidArgs) {
+				return fmt.Errorf("invalid argument %q for %q%s", v, cmd.CommandPath(), cmd.findSuggestions(args[0]))
+			}
+		}
+	}
+	return nil
+}
+
+func stringInSlice(a string, list []string) bool {
+	for _, b := range list {
+		if b == a {
+			return true
+		}
+	}
+	return false
+}
+
+// ArbitraryArgs never returns an error
+func ArbitraryArgs(cmd *Command, args []string) error {
+	return nil
+}
+
+// MinimumNArgs returns an error if there is not at least N args
+func MinimumNArgs(n int) PositionalArgs {
+	return func(cmd *Command, args []string) error {
+		if len(args) < n {
+			return fmt.Errorf("requires at least %d arg(s), only received %d", n, len(args))
+		}
+		return nil
+	}
+}
+
+// MaximumNArgs returns an error if there are more than N args
+func MaximumNArgs(n int) PositionalArgs {
+	return func(cmd *Command, args []string) error {
+		if len(args) > n {
+			return fmt.Errorf("accepts at most %d arg(s), received %d", n, len(args))
+		}
+		return nil
+	}
+}
+
+// ExactArgs returns an error if there are not exactly n args 
+func ExactArgs(n int) PositionalArgs {
+	return func(cmd *Command, args []string) error {
+		if len(args) != n {
+			return fmt.Errorf("accepts %d arg(s), received %d", n, len(args))
+		}
+		return nil
+	}
+}
+
+// RangeArgs returns an error if the number of args is not within the expected range 
+func RangeArgs(min int, max int) PositionalArgs {
+	return func(cmd *Command, args []string) error {
+		if len(args) < min || len(args) > max {
+			return fmt.Errorf("accepts between %d and %d arg(s), received %d", min, max, len(args))
+		}
+		return nil
+	}
+}

+ 56 - 23
vendor/src/github.com/spf13/cobra/command.go

@@ -50,6 +50,8 @@ type Command struct {
 	// List of aliases for ValidArgs. These are not suggested to the user in the bash
 	// completion, but accepted if entered manually.
 	ArgAliases []string
+	// Expected arguments
+	Args PositionalArgs
 	// Custom functions used by the bash autocompletion generator
 	BashCompletionFunction string
 	// Is this command deprecated and should print this string when used?
@@ -110,6 +112,7 @@ type Command struct {
 	output        *io.Writer               // nil means stderr; use Out() method instead
 	usageFunc     func(*Command) error     // Usage can be defined by application
 	usageTemplate string                   // Can be defined by Application
+	flagErrorFunc func(*Command, error) error
 	helpTemplate  string                   // Can be defined by Application
 	helpFunc      func(*Command, []string) // Help can be defined by application
 	helpCommand   *Command                 // The help command
@@ -163,6 +166,12 @@ func (c *Command) SetUsageTemplate(s string) {
 	c.usageTemplate = s
 }
 
+// SetFlagErrorFunc sets a function to generate an error when flag parsing
+// fails
+func (c *Command) SetFlagErrorFunc(f func(*Command, error) error) {
+	c.flagErrorFunc = f
+}
+
 // Can be defined by Application
 func (c *Command) SetHelpFunc(f func(*Command, []string)) {
 	c.helpFunc = f
@@ -224,6 +233,22 @@ func (c *Command) HelpFunc() func(*Command, []string) {
 	}
 }
 
+// FlagErrorFunc returns either the function set by SetFlagErrorFunc for this
+// command or a parent, or it returns a function which returns the original
+// error.
+func (c *Command) FlagErrorFunc() (f func(*Command, error) error) {
+	if c.flagErrorFunc != nil {
+		return c.flagErrorFunc
+	}
+
+	if c.HasParent() {
+		return c.parent.FlagErrorFunc()
+	}
+	return func(c *Command, err error) error {
+		return err
+	}
+}
+
 var minUsagePadding = 25
 
 func (c *Command) UsagePadding() int {
@@ -422,31 +447,27 @@ func (c *Command) Find(args []string) (*Command, []string, error) {
 	}
 
 	commandFound, a := innerfind(c, args)
-	argsWOflags := stripFlags(a, commandFound)
-
-	// no subcommand, always take args
-	if !commandFound.HasSubCommands() {
-		return commandFound, a, nil
+	if commandFound.Args == nil {
+		return commandFound, a, legacyArgs(commandFound, stripFlags(a, commandFound))
 	}
+	return commandFound, a, nil
+}
 
-	// root command with subcommands, do subcommand checking
-	if commandFound == c && len(argsWOflags) > 0 {
-		suggestionsString := ""
-		if !c.DisableSuggestions {
-			if c.SuggestionsMinimumDistance <= 0 {
-				c.SuggestionsMinimumDistance = 2
-			}
-			if suggestions := c.SuggestionsFor(argsWOflags[0]); len(suggestions) > 0 {
-				suggestionsString += "\n\nDid you mean this?\n"
-				for _, s := range suggestions {
-					suggestionsString += fmt.Sprintf("\t%v\n", s)
-				}
-			}
+func (c *Command) findSuggestions(arg string) string {
+	if c.DisableSuggestions {
+		return ""
+	}
+	if c.SuggestionsMinimumDistance <= 0 {
+		c.SuggestionsMinimumDistance = 2
+	}
+	suggestionsString := ""
+	if suggestions := c.SuggestionsFor(arg); len(suggestions) > 0 {
+		suggestionsString += "\n\nDid you mean this?\n"
+		for _, s := range suggestions {
+			suggestionsString += fmt.Sprintf("\t%v\n", s)
 		}
-		return commandFound, a, fmt.Errorf("unknown command %q for %q%s", argsWOflags[0], commandFound.CommandPath(), suggestionsString)
 	}
-
-	return commandFound, a, nil
+	return suggestionsString
 }
 
 func (c *Command) SuggestionsFor(typedName string) []string {
@@ -520,7 +541,7 @@ func (c *Command) execute(a []string) (err error) {
 
 	err = c.ParseFlags(a)
 	if err != nil {
-		return err
+		return c.FlagErrorFunc()(c, err)
 	}
 	// If help is called, regardless of other flags, return we want help
 	// Also say we need help if the command isn't runnable.
@@ -535,6 +556,10 @@ func (c *Command) execute(a []string) (err error) {
 		return flag.ErrHelp
 	}
 
+	if err := c.ValidateArgs(a); err != nil {
+		return err
+	}
+
 	c.preRun()
 	argWoFlags := c.Flags().Args()
 
@@ -673,7 +698,15 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
 	return cmd, nil
 }
 
+func (c *Command) ValidateArgs(args []string) error {
+	if c.Args == nil {
+		return nil
+	}
+	return c.Args(c, stripFlags(args, c))
+}
+
 func (c *Command) initHelpFlag() {
+	c.mergePersistentFlags()
 	if c.Flags().Lookup("help") == nil {
 		c.Flags().BoolP("help", "h", false, "help for "+c.Name())
 	}
@@ -747,7 +780,7 @@ func (c *Command) AddCommand(cmds ...*Command) {
 	}
 }
 
-// AddCommand removes one or more commands from a parent command.
+// RemoveCommand removes one or more commands from a parent command.
 func (c *Command) RemoveCommand(cmds ...*Command) {
 	commands := []*Command{}
 main: