diff --git a/command.go b/command.go index 34d1bf36..c930033b 100644 --- a/command.go +++ b/command.go @@ -27,6 +27,24 @@ import ( flag "github.com/spf13/pflag" ) +// CmdErrorType command error type +type CmdErrorType int + +const ( + // ErrHelp is like flag.ErrHelp + ErrHelp CmdErrorType = iota + // RunError error returned during a Run function + RunError + // CommandError error during cmd execution or flag parsing + CommandError +) + +// CmdError command executed error type +type CmdError struct { + error + CmdErrorType +} + // FParseErrWhitelist configures Flag parse errors to be ignored type FParseErrWhitelist flag.ParseErrorsWhitelist @@ -672,9 +690,9 @@ func (c *Command) ArgsLenAtDash() int { return c.Flags().ArgsLenAtDash() } -func (c *Command) execute(a []string) (err error) { +func (c *Command) execute(a []string) *CmdError { if c == nil { - return fmt.Errorf("Called Execute() on a nil Command") + return &CmdError{fmt.Errorf("Called Execute() on a nil Command"), CommandError} } if len(c.Deprecated) > 0 { @@ -686,9 +704,9 @@ func (c *Command) execute(a []string) (err error) { c.InitDefaultHelpFlag() c.InitDefaultVersionFlag() - err = c.ParseFlags(a) + err := c.ParseFlags(a) if err != nil { - return c.FlagErrorFunc()(c, err) + return &CmdError{c.FlagErrorFunc()(c, err), CommandError} } // If help is called, regardless of other flags, return we want help. @@ -698,11 +716,11 @@ func (c *Command) execute(a []string) (err error) { // should be impossible to get here as we always declare a help // flag in InitDefaultHelpFlag() c.Println("\"help\" flag declared as non-bool. Please correct your code") - return err + return &CmdError{err, CommandError} } if helpVal { - return flag.ErrHelp + return &CmdError{flag.ErrHelp, ErrHelp} } // for back-compat, only add version flag behavior if version is defined @@ -710,19 +728,19 @@ func (c *Command) execute(a []string) (err error) { versionVal, err := c.Flags().GetBool("version") if err != nil { c.Println("\"version\" flag declared as non-bool. Please correct your code") - return err + return &CmdError{err, CommandError} } if versionVal { err := tmpl(c.OutOrStdout(), c.VersionTemplate(), c) if err != nil { c.Println(err) } - return err + return &CmdError{err, CommandError} } } if !c.Runnable() { - return flag.ErrHelp + return &CmdError{flag.ErrHelp, ErrHelp} } c.preRun() @@ -733,13 +751,13 @@ func (c *Command) execute(a []string) (err error) { } if err := c.ValidateArgs(argWoFlags); err != nil { - return err + return &CmdError{err, CommandError} } for p := c; p != nil; p = p.Parent() { if p.PersistentPreRunE != nil { if err := p.PersistentPreRunE(c, argWoFlags); err != nil { - return err + return &CmdError{err, RunError} } break } else if p.PersistentPreRun != nil { @@ -749,25 +767,25 @@ func (c *Command) execute(a []string) (err error) { } if c.PreRunE != nil { if err := c.PreRunE(c, argWoFlags); err != nil { - return err + return &CmdError{err, RunError} } } else if c.PreRun != nil { c.PreRun(c, argWoFlags) } if err := c.validateRequiredFlags(); err != nil { - return err + return &CmdError{err, CommandError} } if c.RunE != nil { if err := c.RunE(c, argWoFlags); err != nil { - return err + return &CmdError{err, RunError} } } else { c.Run(c, argWoFlags) } if c.PostRunE != nil { if err := c.PostRunE(c, argWoFlags); err != nil { - return err + return &CmdError{err, RunError} } } else if c.PostRun != nil { c.PostRun(c, argWoFlags) @@ -775,7 +793,7 @@ func (c *Command) execute(a []string) (err error) { for p := c; p != nil; p = p.Parent() { if p.PersistentPostRunE != nil { if err := p.PersistentPostRunE(c, argWoFlags); err != nil { - return err + return &CmdError{err, RunError} } break } else if p.PersistentPostRun != nil { @@ -849,19 +867,23 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { cmd.commandCalledAs.name = cmd.Name() } - err = cmd.execute(flags) - if err != nil { + cmdErr := cmd.execute(flags) + if cmdErr != nil { // Always show help if requested, even if SilenceErrors is in // effect - if err == flag.ErrHelp { + if cmdErr.CmdErrorType == ErrHelp { cmd.HelpFunc()(cmd, args) return cmd, nil } + if cmdErr.CmdErrorType == RunError { + return cmd, cmdErr + } + // If root command has SilentErrors flagged, // all subcommands should respect it if !cmd.SilenceErrors && !c.SilenceErrors { - c.Println("Error:", err.Error()) + c.Println("Error:", cmdErr) } // If root command has SilentUsage flagged, @@ -869,6 +891,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { if !cmd.SilenceUsage && !c.SilenceUsage { c.Println(cmd.UsageString()) } + return cmd, cmdErr } return cmd, err }