aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/flag.go310
-rw-r--r--cmd/format.go13
-rw-r--r--cmd/run.go443
3 files changed, 386 insertions, 380 deletions
diff --git a/cmd/flag.go b/cmd/flag.go
index 24fd86d..8a9d412 100644
--- a/cmd/flag.go
+++ b/cmd/flag.go
@@ -1,155 +1,155 @@
-/*
- * flag.go - Definitions for flags and associated formatting functions.
- *
- * Copyright 2017 Google Inc.
- * Author: Joe Richey (joerichey@google.com)
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package cmd
-
-import (
- "flag"
- "fmt"
- "strconv"
- "time"
-)
-
-// Useful flags that can be used with a variety of commands.
-var (
- HelpFlag = &BoolFlag{
- Name: "help",
- ShortName: 'h',
- Usage: "Prints a help text for any command or sub-command.",
- }
- VerboseFlag = &BoolFlag{
- Name: "verbose",
- Usage: "Prints additional debug messages.",
- }
- QuietFlag = &BoolFlag{
- Name: "quiet",
- Usage: `Prints nothing except for errors and uses any default
- option instead of prompting the user.`,
- }
- ForceFlag = &BoolFlag{
- Name: "force",
- Usage: `Print no confirmation prompts or warnings and
- automatically proceed with the requested action.`,
- }
-)
-
-// Flag represents a flag that can be passed to a command. The Name, ArgName,
-// and Usage are used to format and display the flag.
-type Flag interface {
- // String formats the flag as either "--name" or "--name=<argName>".
- fmt.Stringer
- // FullUsage is the usage for this flag with an optional default note.
- FullUsage() string
- // Apply sets up this flag on a flag set.
- Apply(*flag.FlagSet)
-}
-
-// Formats as "--name" or as "--name=<argName>" if argName is present.
-func flagFormatHelper(name, argName string) string {
- if argName != "" {
- return fmt.Sprintf("--%s=<%s>", name, argName)
- }
- return fmt.Sprintf("--%s", name)
-}
-
-// Appends (default: <default>) to the usage if defaultString is present.
-func flagUsageHelper(usage, defaultString string) string {
- if defaultString != "" {
- usage += fmt.Sprintf(" (default: %s)", defaultString)
- }
- return usage
-}
-
-// BoolFlag is a Flag of type bool.
-type BoolFlag struct {
- Name string
- ShortName byte
- Usage string
- Default bool
- Value bool
-}
-
-// String always uses the smaller format, as it has no ArgName.
-func (f *BoolFlag) String() string {
- name := f.Name
- if f.ShortName != 0 {
- name += ", -" + string(f.ShortName)
- }
- return flagFormatHelper(name, "")
-}
-
-// FullUsage shows the default if it's true (flag is implicitly passed).
-func (f *BoolFlag) FullUsage() string {
- if !f.Default {
- return flagUsageHelper(f.Usage, "")
- }
- return flagUsageHelper(f.Usage, "true")
-}
-
-// Apply uses BoolFlag's value to set a flag.BoolVar on the FlagSet.
-func (f *BoolFlag) Apply(s *flag.FlagSet) {
- s.BoolVar(&f.Value, f.Name, f.Default, f.Usage)
- if f.ShortName != 0 {
- s.BoolVar(&f.Value, string(f.ShortName), f.Default, f.Usage)
- }
-}
-
-// StringFlag is a Flag of type string.
-type StringFlag struct {
- Name string
- ArgName string
- Usage string
- Default string
- Value string
-}
-
-func (f *StringFlag) String() string { return flagFormatHelper(f.Name, f.ArgName) }
-
-// FullUsage shows the deafult if the string is non-empty.
-func (f *StringFlag) FullUsage() string {
- if f.Default == "" {
- return flagUsageHelper(f.Usage, "")
- }
- return flagUsageHelper(f.Usage, strconv.Quote(f.Default))
-}
-
-// Apply uses StringFlag's value to set a flag.StringVar on the FlagSet.
-func (f *StringFlag) Apply(s *flag.FlagSet) { s.StringVar(&f.Value, f.Name, f.Default, f.Usage) }
-
-// DurationFlag is a Flag of type time.Duration.
-type DurationFlag struct {
- Name string
- ArgName string
- Usage string
- Default time.Duration
- Value time.Duration
-}
-
-func (f *DurationFlag) String() string { return flagFormatHelper(f.Name, f.ArgName) }
-
-// FullUsage shows the default if the duration is non-zero.
-func (f *DurationFlag) FullUsage() string {
- if f.Default == 0 {
- return flagUsageHelper(f.Usage, "")
- }
- return flagUsageHelper(f.Usage, f.Default.String())
-}
-
-// Apply uses DurationFlag's value to set a flag.DurationVar on the FlagSet.
-func (f *DurationFlag) Apply(s *flag.FlagSet) { s.DurationVar(&f.Value, f.Name, f.Default, f.Usage) }
+/*
+ * flag.go - Definitions for flags and associated formatting functions.
+ *
+ * Copyright 2017 Google Inc.
+ * Author: Joe Richey (joerichey@google.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package cmd
+
+import (
+ "flag"
+ "fmt"
+ "strconv"
+ "time"
+)
+
+// Useful flags that can be used with a variety of commands.
+var (
+ HelpFlag = &BoolFlag{
+ Name: "help",
+ ShortName: 'h',
+ Usage: "Prints a help text for any command or sub-command.",
+ }
+ VerboseFlag = &BoolFlag{
+ Name: "verbose",
+ Usage: "Prints additional debug messages.",
+ }
+ QuietFlag = &BoolFlag{
+ Name: "quiet",
+ Usage: `Prints nothing except for errors and uses any default
+ option instead of prompting the user.`,
+ }
+ ForceFlag = &BoolFlag{
+ Name: "force",
+ Usage: `Print no confirmation prompts or warnings and
+ automatically proceed with the requested action.`,
+ }
+)
+
+// Flag represents a flag that can be passed to a command. The Name, ArgName,
+// and Usage are used to format and display the flag.
+type Flag interface {
+ // String formats the flag as either "--name" or "--name=<argName>".
+ fmt.Stringer
+ // FullUsage is the usage for this flag with an optional default note.
+ FullUsage() string
+ // Apply sets up this flag on a flag set.
+ Apply(*flag.FlagSet)
+}
+
+// Formats as "--name" or as "--name=<argName>" if argName is present.
+func flagFormatHelper(name, argName string) string {
+ if argName != "" {
+ return fmt.Sprintf("--%s=<%s>", name, argName)
+ }
+ return fmt.Sprintf("--%s", name)
+}
+
+// Appends (default: <default>) to the usage if defaultString is present.
+func flagUsageHelper(usage, defaultString string) string {
+ if defaultString != "" {
+ usage += fmt.Sprintf(" (default: %s)", defaultString)
+ }
+ return usage
+}
+
+// BoolFlag is a Flag of type bool.
+type BoolFlag struct {
+ Name string
+ ShortName byte
+ Usage string
+ Default bool
+ Value bool
+}
+
+// String always uses the smaller format, as it has no ArgName.
+func (f *BoolFlag) String() string {
+ name := f.Name
+ if f.ShortName != 0 {
+ name += ", -" + string(f.ShortName)
+ }
+ return flagFormatHelper(name, "")
+}
+
+// FullUsage shows the default if it's true (flag is implicitly passed).
+func (f *BoolFlag) FullUsage() string {
+ if !f.Default {
+ return flagUsageHelper(f.Usage, "")
+ }
+ return flagUsageHelper(f.Usage, "true")
+}
+
+// Apply uses BoolFlag's value to set a flag.BoolVar on the FlagSet.
+func (f *BoolFlag) Apply(s *flag.FlagSet) {
+ s.BoolVar(&f.Value, f.Name, f.Default, f.Usage)
+ if f.ShortName != 0 {
+ s.BoolVar(&f.Value, string(f.ShortName), f.Default, f.Usage)
+ }
+}
+
+// StringFlag is a Flag of type string.
+type StringFlag struct {
+ Name string
+ ArgName string
+ Usage string
+ Default string
+ Value string
+}
+
+func (f *StringFlag) String() string { return flagFormatHelper(f.Name, f.ArgName) }
+
+// FullUsage shows the deafult if the string is non-empty.
+func (f *StringFlag) FullUsage() string {
+ if f.Default == "" {
+ return flagUsageHelper(f.Usage, "")
+ }
+ return flagUsageHelper(f.Usage, strconv.Quote(f.Default))
+}
+
+// Apply uses StringFlag's value to set a flag.StringVar on the FlagSet.
+func (f *StringFlag) Apply(s *flag.FlagSet) { s.StringVar(&f.Value, f.Name, f.Default, f.Usage) }
+
+// DurationFlag is a Flag of type time.Duration.
+type DurationFlag struct {
+ Name string
+ ArgName string
+ Usage string
+ Default time.Duration
+ Value time.Duration
+}
+
+func (f *DurationFlag) String() string { return flagFormatHelper(f.Name, f.ArgName) }
+
+// FullUsage shows the default if the duration is non-zero.
+func (f *DurationFlag) FullUsage() string {
+ if f.Default == 0 {
+ return flagUsageHelper(f.Usage, "")
+ }
+ return flagUsageHelper(f.Usage, f.Default.String())
+}
+
+// Apply uses DurationFlag's value to set a flag.DurationVar on the FlagSet.
+func (f *DurationFlag) Apply(s *flag.FlagSet) { s.DurationVar(&f.Value, f.Name, f.Default, f.Usage) }
diff --git a/cmd/format.go b/cmd/format.go
index 993b955..2953529 100644
--- a/cmd/format.go
+++ b/cmd/format.go
@@ -66,8 +66,8 @@ func init() {
}
}
-// MaxSubcommandLength returns the length of the longest subcommand (where the
-// length of the command is Name + Title). Return 0 if there aren't subcommands.
+// MaxNameLength returns the length of the longest subcommand Name. Return 0 if
+// there aren't subcommands.
func (c *Command) MaxNameLength() (max int) {
for _, s := range c.SubCommands {
max = util.MaxInt(max, len(s.Name))
@@ -75,6 +75,15 @@ func (c *Command) MaxNameLength() (max int) {
return
}
+// MaxTitleLength returns the length of the longest subcommand Title. Return 0
+// if there aren't subcommands.
+func (c *Command) MaxTitleLength() (max int) {
+ for _, s := range c.SubCommands {
+ max = util.MaxInt(max, len(s.Title))
+ }
+ return
+}
+
// WrapText wraps an input string so that each line begins with numTabs tabs
// (except the first line) and ends with a newline (except the last line), and
// each line has length less than lineLength. If the text contains a word which
diff --git a/cmd/run.go b/cmd/run.go
index 7517662..693cebd 100644
--- a/cmd/run.go
+++ b/cmd/run.go
@@ -1,223 +1,220 @@
-/*
- * run.go - Functions to setup and run Command Actions.
- *
- * Copyright 2017 Google Inc.
- * Author: Joe Richey (joerichey@google.com)
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package cmd
-
-import (
- "flag"
- "fmt"
- "io/ioutil"
- "log"
- "os"
-)
-
-var (
- // HelpAction is performed whenever a Command uses the HelpFlag.
- HelpAction Action = func(ctx *Context) error {
- ExecuteTemplate(os.Stdout, TemplateTitle, ctx)
- ExecuteTemplate(os.Stdout, TemplateUsage, ctx)
- return nil
- }
- // DefaultAction is preformed when a Command has no Action specified.
- DefaultAction = HelpAction
- // stopFlag indicates that everything after the flag is an argument, not
- // a command or flag.
- stopFlag = "--"
-)
-
-// TemplateTitle describes the format of a Command's one line title.
-var TemplateTitle = "{{.FullName}}{{if .Command.Title}} - {{.Command.Title}}{{end}}\n"
-
-// TemplateUsage describes the format of a Command's usage.
-var TemplateUsage = `{{with $lines := .Command.UsageLines}}
-Usage:
-{{- range $lines}}
- {{printf "%s %s" $.FullName . | WrapText 8 2 -}}
-{{end}}
-{{end -}}
-
-{{with $commands := .Command.SubCommands}}
-{{with $n := $.Command.MaxNameLength -}}
-{{with $fmt := printf "%%-%ds - %%s" $n -}}
-Commands:
-{{if le (add 8 $n 3 $.Command.MaxTitleLength) LineLength -}}
-
-{{range $commands -}}
-
-{{end -}}
-
-{{else -}}
-
-{{range $commands}}
-{{end -}}
-
-{{end}}{{end}}{{end}}
-{{end -}}
-
-
-{{- range $commands}}
- {{if not .Title -}}
- {{.Name -}}
- {{else if le (add 8 $n 3 (len .Title)) LineLength -}}
- {{printf $fmt .Name .Title -}}
- {{else -}}
- {{.Name}}
- {{WrapText 16 2 .Title -}}
- {{end -}}
-{{end}}{{end}}{{end}}
-{{end -}}
-
-{{with $arguments := .FullArguments}}
-Arguments:
-{{- range $arguments}}
- {{.}}
- {{WrapText 16 2 .Usage -}}
-{{end}}
-{{end -}}
-
-{{with $flags := .FullFlags}}
-Options:
-{{- range $flags}}
- {{.}}
- {{WrapText 16 2 .FullUsage -}}
-{{end}}
-{{end -}}
-
-{{with .ManPage}}
-For more information, see {{.}}.
-{{end -}}
-`
-
-// Divide the arguments into flag arguments (those starting with "-") and normal
-// arguments. If "--" appears in the list, it will classified as a normal
-// argument as well as all arguments following it. Also removes empty args.
-func sortArgs(args []string) (normalArgs, flagArgs []string) {
- var arg string
- for len(args) > 0 {
- arg, args = args[0], args[1:]
- if arg == "" {
- continue
- }
- if arg == stopFlag {
- normalArgs = append(normalArgs, arg)
- normalArgs = append(normalArgs, args...)
- return
- } else if arg[0] == '-' {
- flagArgs = append(flagArgs, arg)
- } else {
- normalArgs = append(normalArgs, arg)
- }
- }
- return
-}
-
-// Returns the name of the requested sub-command or empty string (if a
-// sub-command was not requested).
-func subCommandName(ctx *Context) string {
- // We must have actual arguments and subcommands to run a sub-command.
- if len(ctx.Command.SubCommands) == 0 || len(ctx.Args) == 0 {
- return ""
- }
- name := ctx.Args[0]
- if name == stopFlag {
- return ""
- }
- return name
-}
-
-// Returns the appropriate child context with a sub-command whose name matches
-// the provided name. If no sub-commands match the provied name, handle the
-// appropriate error and do not return.
-func getSubContext(ctx *Context, name string) *Context {
- for _, subCommand := range ctx.Command.SubCommands {
- if subCommand.Name == name {
- return &Context{
- Command: subCommand,
- Parent: ctx,
- Args: ctx.Args[1:],
- flagArgs: ctx.flagArgs,
- }
- }
- }
- ctx.processError(UsageError(fmt.Sprintf("unknown command %q", name)))
- return nil
-}
-
-// Configures the Output and log output io.Writers. Called before running
-// commands but after processing flags.
-func setupOutput() {
- if VerboseFlag.Value {
- log.SetOutput(os.Stdout)
- } else {
- log.SetOutput(ioutil.Discard)
- }
- if Output != nil {
- return
- }
- if QuietFlag.Value {
- Output = ioutil.Discard
- } else {
- Output = os.Stdout
- }
-}
-
-// Remove the stopFlag from the args if it is present. Args are modified
-// in-place and the correctly sized slice is returned.
-func setupArgs(args []string) []string {
- for i, arg := range args {
- if arg == stopFlag {
- return append(args[:i], args[i+1:]...)
- }
- }
- return args
-}
-
-// Return a command's action, the HelpAction, or DefaultAction.
-func getAction(cmd *Command) Action {
- if HelpFlag.Value {
- return HelpAction
- }
- if cmd.Action == nil {
- return DefaultAction
- }
- return cmd.Action
-}
-
-func (ctx *Context) run() {
- if name := subCommandName(ctx); name != "" {
- getSubContext(ctx, name).run()
- return
- }
-
- flagSet := flag.NewFlagSet("", flag.ContinueOnError)
- flagSet.SetOutput(ioutil.Discard)
- for _, flag := range ctx.FullFlags() {
- flag.Apply(flagSet)
- }
- if err := flagSet.Parse(ctx.flagArgs); err != nil {
- ctx.processError(err)
- return
- }
-
- setupOutput()
- ctx.Args = setupArgs(ctx.Args)
- action := getAction(ctx.Command)
-
- ctx.processError(action(ctx))
-}
+/*
+ * run.go - Functions to setup and run Command Actions.
+ *
+ * Copyright 2017 Google Inc.
+ * Author: Joe Richey (joerichey@google.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package cmd
+
+import (
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+)
+
+var (
+ // HelpAction is performed whenever a Command uses the HelpFlag.
+ HelpAction Action = func(ctx *Context) error {
+ ExecuteTemplate(os.Stdout, TemplateTitle, ctx)
+ ExecuteTemplate(os.Stdout, TemplateUsage, ctx)
+ return nil
+ }
+ // DefaultAction is preformed when a Command has no Action specified.
+ DefaultAction = HelpAction
+ // stopFlag indicates that everything after the flag is an argument, not
+ // a command or flag.
+ stopFlag = "--"
+)
+
+// TemplateTitle describes the format of a Command's one line title.
+var TemplateTitle = "{{.FullName}}{{if .Command.Title}} - {{.Command.Title}}{{end}}\n"
+
+// TemplateUsage describes the format of a Command's usage.
+var TemplateUsage = `{{with $lines := .Command.UsageLines}}
+Usage:
+{{- range $lines}}
+ {{printf "%s %s" $.FullName . | WrapText 8 2 -}}
+{{end}}
+{{end -}}
+
+{{with $commands := .Command.SubCommands}}
+{{with $n := $.Command.MaxNameLength -}}
+{{with $fmt := printf "%%-%ds - %%s" $n -}}
+Commands:
+{{- if le (add 8 $n 3 $.Command.MaxTitleLength) LineLength -}}
+
+{{range $commands -}}
+ {{if not .Title}}
+ {{.Name -}}
+ {{else}}
+ {{printf $fmt .Name .Title -}}
+ {{end -}}
+{{end -}}
+
+{{else -}}
+
+{{range $commands -}}
+ {{if not .Title}}
+ {{.Name -}}
+ {{else}}
+ {{.Name}}
+ {{WrapText 16 2 .Title -}}
+ {{end -}}
+{{end -}}
+
+{{end}}{{end}}{{end}}
+{{end -}}
+
+{{with $arguments := .FullArguments}}
+Arguments:
+{{- range $arguments}}
+ {{.}}
+ {{WrapText 16 2 .Usage -}}
+{{end}}
+{{end -}}
+
+{{with $flags := .FullFlags}}
+Options:
+{{- range $flags}}
+ {{.}}
+ {{WrapText 16 2 .FullUsage -}}
+{{end}}
+{{end -}}
+
+{{with .ManPage}}
+For more information, see {{.}}.
+{{end -}}
+`
+
+// Divide the arguments into flag arguments (those starting with "-") and normal
+// arguments. If "--" appears in the list, it will classified as a normal
+// argument as well as all arguments following it. Also removes empty args.
+func sortArgs(args []string) (normalArgs, flagArgs []string) {
+ var arg string
+ for len(args) > 0 {
+ arg, args = args[0], args[1:]
+ if arg == "" {
+ continue
+ }
+ if arg == stopFlag {
+ normalArgs = append(normalArgs, arg)
+ normalArgs = append(normalArgs, args...)
+ return
+ } else if arg[0] == '-' {
+ flagArgs = append(flagArgs, arg)
+ } else {
+ normalArgs = append(normalArgs, arg)
+ }
+ }
+ return
+}
+
+// Returns the name of the requested sub-command or empty string (if a
+// sub-command was not requested).
+func subCommandName(ctx *Context) string {
+ // We must have actual arguments and subcommands to run a sub-command.
+ if len(ctx.Command.SubCommands) == 0 || len(ctx.Args) == 0 {
+ return ""
+ }
+ name := ctx.Args[0]
+ if name == stopFlag {
+ return ""
+ }
+ return name
+}
+
+// Returns the appropriate child context with a sub-command whose name matches
+// the provided name. If no sub-commands match the provied name, handle the
+// appropriate error and do not return.
+func getSubContext(ctx *Context, name string) *Context {
+ for _, subCommand := range ctx.Command.SubCommands {
+ if subCommand.Name == name {
+ return &Context{
+ Command: subCommand,
+ Parent: ctx,
+ Args: ctx.Args[1:],
+ flagArgs: ctx.flagArgs,
+ }
+ }
+ }
+ ctx.processError(UsageError(fmt.Sprintf("unknown command %q", name)))
+ return nil
+}
+
+// Configures the Output and log output io.Writers. Called before running
+// commands but after processing flags.
+func setupOutput() {
+ if VerboseFlag.Value {
+ log.SetOutput(os.Stdout)
+ } else {
+ log.SetOutput(ioutil.Discard)
+ }
+ if Output != nil {
+ return
+ }
+ if QuietFlag.Value {
+ Output = ioutil.Discard
+ } else {
+ Output = os.Stdout
+ }
+}
+
+// Remove the stopFlag from the args if it is present. Args are modified
+// in-place and the correctly sized slice is returned.
+func setupArgs(args []string) []string {
+ for i, arg := range args {
+ if arg == stopFlag {
+ return append(args[:i], args[i+1:]...)
+ }
+ }
+ return args
+}
+
+// Return a command's action, the HelpAction, or DefaultAction.
+func getAction(cmd *Command) Action {
+ if HelpFlag.Value {
+ return HelpAction
+ }
+ if cmd.Action == nil {
+ return DefaultAction
+ }
+ return cmd.Action
+}
+
+func (ctx *Context) run() {
+ if name := subCommandName(ctx); name != "" {
+ getSubContext(ctx, name).run()
+ return
+ }
+
+ flagSet := flag.NewFlagSet("", flag.ContinueOnError)
+ flagSet.SetOutput(ioutil.Discard)
+ for _, flag := range ctx.FullFlags() {
+ flag.Apply(flagSet)
+ }
+ if err := flagSet.Parse(ctx.flagArgs); err != nil {
+ ctx.processError(err)
+ return
+ }
+
+ setupOutput()
+ ctx.Args = setupArgs(ctx.Args)
+ action := getAction(ctx.Command)
+
+ ctx.processError(action(ctx))
+}