package cobra
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"sort"
"strings"
flag "github.com/spf13/pflag"
)
const (
FlagSetByCobraAnnotation = "cobra_annotation_flag_set_by_cobra"
CommandDisplayNameAnnotation = "cobra_annotation_command_display_name"
helpFlagName = "help"
helpCommandName = "help"
)
type FParseErrWhitelist flag .ParseErrorsWhitelist
type Group struct {
ID string
Title string
}
type Command struct {
Use string
Aliases []string
SuggestFor []string
Short string
GroupID string
Long string
Example string
ValidArgs []Completion
ValidArgsFunction CompletionFunc
Args PositionalArgs
ArgAliases []string
BashCompletionFunction string
Deprecated string
Annotations map [string ]string
Version string
PersistentPreRun func (cmd *Command , args []string )
PersistentPreRunE func (cmd *Command , args []string ) error
PreRun func (cmd *Command , args []string )
PreRunE func (cmd *Command , args []string ) error
Run func (cmd *Command , args []string )
RunE func (cmd *Command , args []string ) error
PostRun func (cmd *Command , args []string )
PostRunE func (cmd *Command , args []string ) error
PersistentPostRun func (cmd *Command , args []string )
PersistentPostRunE func (cmd *Command , args []string ) error
commandgroups []*Group
args []string
flagErrorBuf *bytes .Buffer
flags *flag .FlagSet
pflags *flag .FlagSet
lflags *flag .FlagSet
iflags *flag .FlagSet
parentsPflags *flag .FlagSet
globNormFunc func (f *flag .FlagSet , name string ) flag .NormalizedName
usageFunc func (*Command ) error
usageTemplate *tmplFunc
flagErrorFunc func (*Command , error ) error
helpTemplate *tmplFunc
helpFunc func (*Command , []string )
helpCommand *Command
helpCommandGroupID string
completionCommandGroupID string
versionTemplate *tmplFunc
errPrefix string
inReader io .Reader
outWriter io .Writer
errWriter io .Writer
FParseErrWhitelist FParseErrWhitelist
CompletionOptions CompletionOptions
commandsAreSorted bool
commandCalledAs struct {
name string
called bool
}
ctx context .Context
commands []*Command
parent *Command
commandsMaxUseLen int
commandsMaxCommandPathLen int
commandsMaxNameLen int
TraverseChildren bool
Hidden bool
SilenceErrors bool
SilenceUsage bool
DisableFlagParsing bool
DisableAutoGenTag bool
DisableFlagsInUseLine bool
DisableSuggestions bool
SuggestionsMinimumDistance int
}
func (c *Command ) Context () context .Context {
return c .ctx
}
func (c *Command ) SetContext (ctx context .Context ) {
c .ctx = ctx
}
func (c *Command ) SetArgs (a []string ) {
c .args = a
}
func (c *Command ) SetOutput (output io .Writer ) {
c .outWriter = output
c .errWriter = output
}
func (c *Command ) SetOut (newOut io .Writer ) {
c .outWriter = newOut
}
func (c *Command ) SetErr (newErr io .Writer ) {
c .errWriter = newErr
}
func (c *Command ) SetIn (newIn io .Reader ) {
c .inReader = newIn
}
func (c *Command ) SetUsageFunc (f func (*Command ) error ) {
c .usageFunc = f
}
func (c *Command ) SetUsageTemplate (s string ) {
if s == "" {
c .usageTemplate = nil
return
}
c .usageTemplate = tmpl (s )
}
func (c *Command ) SetFlagErrorFunc (f func (*Command , error ) error ) {
c .flagErrorFunc = f
}
func (c *Command ) SetHelpFunc (f func (*Command , []string )) {
c .helpFunc = f
}
func (c *Command ) SetHelpCommand (cmd *Command ) {
c .helpCommand = cmd
}
func (c *Command ) SetHelpCommandGroupID (groupID string ) {
if c .helpCommand != nil {
c .helpCommand .GroupID = groupID
}
c .helpCommandGroupID = groupID
}
func (c *Command ) SetCompletionCommandGroupID (groupID string ) {
c .Root ().completionCommandGroupID = groupID
}
func (c *Command ) SetHelpTemplate (s string ) {
if s == "" {
c .helpTemplate = nil
return
}
c .helpTemplate = tmpl (s )
}
func (c *Command ) SetVersionTemplate (s string ) {
if s == "" {
c .versionTemplate = nil
return
}
c .versionTemplate = tmpl (s )
}
func (c *Command ) SetErrPrefix (s string ) {
c .errPrefix = s
}
func (c *Command ) SetGlobalNormalizationFunc (n func (f *flag .FlagSet , name string ) flag .NormalizedName ) {
c .Flags ().SetNormalizeFunc (n )
c .PersistentFlags ().SetNormalizeFunc (n )
c .globNormFunc = n
for _ , command := range c .commands {
command .SetGlobalNormalizationFunc (n )
}
}
func (c *Command ) OutOrStdout () io .Writer {
return c .getOut (os .Stdout )
}
func (c *Command ) OutOrStderr () io .Writer {
return c .getOut (os .Stderr )
}
func (c *Command ) ErrOrStderr () io .Writer {
return c .getErr (os .Stderr )
}
func (c *Command ) InOrStdin () io .Reader {
return c .getIn (os .Stdin )
}
func (c *Command ) getOut (def io .Writer ) io .Writer {
if c .outWriter != nil {
return c .outWriter
}
if c .HasParent () {
return c .parent .getOut (def )
}
return def
}
func (c *Command ) getErr (def io .Writer ) io .Writer {
if c .errWriter != nil {
return c .errWriter
}
if c .HasParent () {
return c .parent .getErr (def )
}
return def
}
func (c *Command ) getIn (def io .Reader ) io .Reader {
if c .inReader != nil {
return c .inReader
}
if c .HasParent () {
return c .parent .getIn (def )
}
return def
}
func (c *Command ) UsageFunc () (f func (*Command ) error ) {
if c .usageFunc != nil {
return c .usageFunc
}
if c .HasParent () {
return c .Parent ().UsageFunc ()
}
return func (c *Command ) error {
c .mergePersistentFlags ()
fn := c .getUsageTemplateFunc ()
err := fn (c .OutOrStderr (), c )
if err != nil {
c .PrintErrln (err )
}
return err
}
}
func (c *Command ) getUsageTemplateFunc () func (w io .Writer , data interface {}) error {
if c .usageTemplate != nil {
return c .usageTemplate .fn
}
if c .HasParent () {
return c .parent .getUsageTemplateFunc ()
}
return defaultUsageFunc
}
func (c *Command ) Usage () error {
return c .UsageFunc ()(c )
}
func (c *Command ) HelpFunc () func (*Command , []string ) {
if c .helpFunc != nil {
return c .helpFunc
}
if c .HasParent () {
return c .Parent ().HelpFunc ()
}
return func (c *Command , a []string ) {
c .mergePersistentFlags ()
fn := c .getHelpTemplateFunc ()
err := fn (c .OutOrStdout (), c )
if err != nil {
c .PrintErrln (err )
}
}
}
func (c *Command ) getHelpTemplateFunc () func (w io .Writer , data interface {}) error {
if c .helpTemplate != nil {
return c .helpTemplate .fn
}
if c .HasParent () {
return c .parent .getHelpTemplateFunc ()
}
return defaultHelpFunc
}
func (c *Command ) Help () error {
c .HelpFunc ()(c , []string {})
return nil
}
func (c *Command ) UsageString () string {
tmpOutput := c .outWriter
tmpErr := c .errWriter
bb := new (bytes .Buffer )
c .outWriter = bb
c .errWriter = bb
CheckErr (c .Usage ())
c .outWriter = tmpOutput
c .errWriter = tmpErr
return bb .String ()
}
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 {
if c .parent == nil || minUsagePadding > c .parent .commandsMaxUseLen {
return minUsagePadding
}
return c .parent .commandsMaxUseLen
}
var minCommandPathPadding = 11
func (c *Command ) CommandPathPadding () int {
if c .parent == nil || minCommandPathPadding > c .parent .commandsMaxCommandPathLen {
return minCommandPathPadding
}
return c .parent .commandsMaxCommandPathLen
}
var minNamePadding = 11
func (c *Command ) NamePadding () int {
if c .parent == nil || minNamePadding > c .parent .commandsMaxNameLen {
return minNamePadding
}
return c .parent .commandsMaxNameLen
}
func (c *Command ) UsageTemplate () string {
if c .usageTemplate != nil {
return c .usageTemplate .tmpl
}
if c .HasParent () {
return c .parent .UsageTemplate ()
}
return defaultUsageTemplate
}
func (c *Command ) HelpTemplate () string {
if c .helpTemplate != nil {
return c .helpTemplate .tmpl
}
if c .HasParent () {
return c .parent .HelpTemplate ()
}
return defaultHelpTemplate
}
func (c *Command ) VersionTemplate () string {
if c .versionTemplate != nil {
return c .versionTemplate .tmpl
}
if c .HasParent () {
return c .parent .VersionTemplate ()
}
return defaultVersionTemplate
}
func (c *Command ) getVersionTemplateFunc () func (w io .Writer , data interface {}) error {
if c .versionTemplate != nil {
return c .versionTemplate .fn
}
if c .HasParent () {
return c .parent .getVersionTemplateFunc ()
}
return defaultVersionFunc
}
func (c *Command ) ErrPrefix () string {
if c .errPrefix != "" {
return c .errPrefix
}
if c .HasParent () {
return c .parent .ErrPrefix ()
}
return "Error:"
}
func hasNoOptDefVal(name string , fs *flag .FlagSet ) bool {
flag := fs .Lookup (name )
if flag == nil {
return false
}
return flag .NoOptDefVal != ""
}
func shortHasNoOptDefVal(name string , fs *flag .FlagSet ) bool {
if len (name ) == 0 {
return false
}
flag := fs .ShorthandLookup (name [:1 ])
if flag == nil {
return false
}
return flag .NoOptDefVal != ""
}
func stripFlags(args []string , c *Command ) []string {
if len (args ) == 0 {
return args
}
c .mergePersistentFlags ()
commands := []string {}
flags := c .Flags ()
Loop :
for len (args ) > 0 {
s := args [0 ]
args = args [1 :]
switch {
case s == "--" :
break Loop
case strings .HasPrefix (s , "--" ) && !strings .Contains (s , "=" ) && !hasNoOptDefVal (s [2 :], flags ):
fallthrough
case strings .HasPrefix (s , "-" ) && !strings .Contains (s , "=" ) && len (s ) == 2 && !shortHasNoOptDefVal (s [1 :], flags ):
if len (args ) <= 1 {
break Loop
} else {
args = args [1 :]
continue
}
case s != "" && !strings .HasPrefix (s , "-" ):
commands = append (commands , s )
}
}
return commands
}
func (c *Command ) argsMinusFirstX (args []string , x string ) []string {
if len (args ) == 0 {
return args
}
c .mergePersistentFlags ()
flags := c .Flags ()
Loop :
for pos := 0 ; pos < len (args ); pos ++ {
s := args [pos ]
switch {
case s == "--" :
break Loop
case strings .HasPrefix (s , "--" ) && !strings .Contains (s , "=" ) && !hasNoOptDefVal (s [2 :], flags ):
fallthrough
case strings .HasPrefix (s , "-" ) && !strings .Contains (s , "=" ) && len (s ) == 2 && !shortHasNoOptDefVal (s [1 :], flags ):
pos ++
continue
case !strings .HasPrefix (s , "-" ):
if s == x {
ret := make ([]string , 0 , len (args )-1 )
ret = append (ret , args [:pos ]...)
ret = append (ret , args [pos +1 :]...)
return ret
}
}
}
return args
}
func isFlagArg(arg string ) bool {
return ((len (arg ) >= 3 && arg [0 :2 ] == "--" ) ||
(len (arg ) >= 2 && arg [0 ] == '-' && arg [1 ] != '-' ))
}
func (c *Command ) Find (args []string ) (*Command , []string , error ) {
var innerfind func (*Command , []string ) (*Command , []string )
innerfind = func (c *Command , innerArgs []string ) (*Command , []string ) {
argsWOflags := stripFlags (innerArgs , c )
if len (argsWOflags ) == 0 {
return c , innerArgs
}
nextSubCmd := argsWOflags [0 ]
cmd := c .findNext (nextSubCmd )
if cmd != nil {
return innerfind (cmd , c .argsMinusFirstX (innerArgs , nextSubCmd ))
}
return c , innerArgs
}
commandFound , a := innerfind (c , args )
if commandFound .Args == nil {
return commandFound , a , legacyArgs (commandFound , stripFlags (a , commandFound ))
}
return commandFound , a , nil
}
func (c *Command ) findSuggestions (arg string ) string {
if c .DisableSuggestions {
return ""
}
if c .SuggestionsMinimumDistance <= 0 {
c .SuggestionsMinimumDistance = 2
}
var sb strings .Builder
if suggestions := c .SuggestionsFor (arg ); len (suggestions ) > 0 {
sb .WriteString ("\n\nDid you mean this?\n" )
for _ , s := range suggestions {
_, _ = fmt .Fprintf (&sb , "\t%v\n" , s )
}
}
return sb .String ()
}
func (c *Command ) findNext (next string ) *Command {
matches := make ([]*Command , 0 )
for _ , cmd := range c .commands {
if commandNameMatches (cmd .Name (), next ) || cmd .HasAlias (next ) {
cmd .commandCalledAs .name = next
return cmd
}
if EnablePrefixMatching && cmd .hasNameOrAliasPrefix (next ) {
matches = append (matches , cmd )
}
}
if len (matches ) == 1 {
return matches [0 ]
}
return nil
}
func (c *Command ) Traverse (args []string ) (*Command , []string , error ) {
flags := []string {}
inFlag := false
for i , arg := range args {
switch {
case strings .HasPrefix (arg , "--" ) && !strings .Contains (arg , "=" ):
inFlag = !hasNoOptDefVal (arg [2 :], c .Flags ())
flags = append (flags , arg )
continue
case strings .HasPrefix (arg , "-" ) && !strings .Contains (arg , "=" ) && len (arg ) == 2 && !shortHasNoOptDefVal (arg [1 :], c .Flags ()):
inFlag = true
flags = append (flags , arg )
continue
case inFlag :
inFlag = false
flags = append (flags , arg )
continue
case isFlagArg (arg ):
flags = append (flags , arg )
continue
}
cmd := c .findNext (arg )
if cmd == nil {
return c , args , nil
}
if err := c .ParseFlags (flags ); err != nil {
return nil , args , err
}
return cmd .Traverse (args [i +1 :])
}
return c , args , nil
}
func (c *Command ) SuggestionsFor (typedName string ) []string {
suggestions := []string {}
for _ , cmd := range c .commands {
if cmd .IsAvailableCommand () {
levenshteinDistance := ld (typedName , cmd .Name (), true )
suggestByLevenshtein := levenshteinDistance <= c .SuggestionsMinimumDistance
suggestByPrefix := strings .HasPrefix (strings .ToLower (cmd .Name ()), strings .ToLower (typedName ))
if suggestByLevenshtein || suggestByPrefix {
suggestions = append (suggestions , cmd .Name ())
}
for _ , explicitSuggestion := range cmd .SuggestFor {
if strings .EqualFold (typedName , explicitSuggestion ) {
suggestions = append (suggestions , cmd .Name ())
}
}
}
}
return suggestions
}
func (c *Command ) VisitParents (fn func (*Command )) {
if c .HasParent () {
fn (c .Parent ())
c .Parent ().VisitParents (fn )
}
}
func (c *Command ) Root () *Command {
if c .HasParent () {
return c .Parent ().Root ()
}
return c
}
func (c *Command ) ArgsLenAtDash () int {
return c .Flags ().ArgsLenAtDash ()
}
func (c *Command ) execute (a []string ) (err error ) {
if c == nil {
return fmt .Errorf ("called Execute() on a nil Command" )
}
if len (c .Deprecated ) > 0 {
c .Printf ("Command %q is deprecated, %s\n" , c .Name (), c .Deprecated )
}
c .InitDefaultHelpFlag ()
c .InitDefaultVersionFlag ()
err = c .ParseFlags (a )
if err != nil {
return c .FlagErrorFunc ()(c , err )
}
helpVal , err := c .Flags ().GetBool (helpFlagName )
if err != nil {
c .Println ("\"help\" flag declared as non-bool. Please correct your code" )
return err
}
if helpVal {
return flag .ErrHelp
}
if c .Version != "" {
versionVal , err := c .Flags ().GetBool ("version" )
if err != nil {
c .Println ("\"version\" flag declared as non-bool. Please correct your code" )
return err
}
if versionVal {
fn := c .getVersionTemplateFunc ()
err := fn (c .OutOrStdout (), c )
if err != nil {
c .Println (err )
}
return err
}
}
if !c .Runnable () {
return flag .ErrHelp
}
c .preRun ()
defer c .postRun ()
argWoFlags := c .Flags ().Args ()
if c .DisableFlagParsing {
argWoFlags = a
}
if err := c .ValidateArgs (argWoFlags ); err != nil {
return err
}
parents := make ([]*Command , 0 , 5 )
for p := c ; p != nil ; p = p .Parent () {
if EnableTraverseRunHooks {
parents = append ([]*Command {p }, parents ...)
} else {
parents = append (parents , p )
}
}
for _ , p := range parents {
if p .PersistentPreRunE != nil {
if err := p .PersistentPreRunE (c , argWoFlags ); err != nil {
return err
}
if !EnableTraverseRunHooks {
break
}
} else if p .PersistentPreRun != nil {
p .PersistentPreRun (c , argWoFlags )
if !EnableTraverseRunHooks {
break
}
}
}
if c .PreRunE != nil {
if err := c .PreRunE (c , argWoFlags ); err != nil {
return err
}
} else if c .PreRun != nil {
c .PreRun (c , argWoFlags )
}
if err := c .ValidateRequiredFlags (); err != nil {
return err
}
if err := c .ValidateFlagGroups (); err != nil {
return err
}
if c .RunE != nil {
if err := c .RunE (c , argWoFlags ); err != nil {
return err
}
} else {
c .Run (c , argWoFlags )
}
if c .PostRunE != nil {
if err := c .PostRunE (c , argWoFlags ); err != nil {
return err
}
} else if c .PostRun != nil {
c .PostRun (c , argWoFlags )
}
for p := c ; p != nil ; p = p .Parent () {
if p .PersistentPostRunE != nil {
if err := p .PersistentPostRunE (c , argWoFlags ); err != nil {
return err
}
if !EnableTraverseRunHooks {
break
}
} else if p .PersistentPostRun != nil {
p .PersistentPostRun (c , argWoFlags )
if !EnableTraverseRunHooks {
break
}
}
}
return nil
}
func (c *Command ) preRun () {
for _ , x := range initializers {
x ()
}
}
func (c *Command ) postRun () {
for _ , x := range finalizers {
x ()
}
}
func (c *Command ) ExecuteContext (ctx context .Context ) error {
c .ctx = ctx
return c .Execute ()
}
func (c *Command ) Execute () error {
_ , err := c .ExecuteC ()
return err
}
func (c *Command ) ExecuteContextC (ctx context .Context ) (*Command , error ) {
c .ctx = ctx
return c .ExecuteC ()
}
func (c *Command ) ExecuteC () (cmd *Command , err error ) {
if c .ctx == nil {
c .ctx = context .Background ()
}
if c .HasParent () {
return c .Root ().ExecuteC ()
}
if preExecHookFn != nil {
preExecHookFn (c )
}
c .InitDefaultHelpCmd ()
args := c .args
if c .args == nil && filepath .Base (os .Args [0 ]) != "cobra.test" {
args = os .Args [1 :]
}
c .initCompleteCmd (args )
c .InitDefaultCompletionCmd (args ...)
c .checkCommandGroups ()
var flags []string
if c .TraverseChildren {
cmd , flags , err = c .Traverse (args )
} else {
cmd , flags , err = c .Find (args )
}
if err != nil {
if cmd != nil {
c = cmd
}
if !c .SilenceErrors {
c .PrintErrln (c .ErrPrefix (), err .Error())
c .PrintErrf ("Run '%v --help' for usage.\n" , c .CommandPath ())
}
return c , err
}
cmd .commandCalledAs .called = true
if cmd .commandCalledAs .name == "" {
cmd .commandCalledAs .name = cmd .Name ()
}
if cmd .ctx == nil {
cmd .ctx = c .ctx
}
err = cmd .execute (flags )
if err != nil {
if errors .Is (err , flag .ErrHelp ) {
cmd .HelpFunc ()(cmd , args )
return cmd , nil
}
if !cmd .SilenceErrors && !c .SilenceErrors {
c .PrintErrln (cmd .ErrPrefix (), err .Error())
}
if !cmd .SilenceUsage && !c .SilenceUsage {
c .Println (cmd .UsageString ())
}
}
return cmd , err
}
func (c *Command ) ValidateArgs (args []string ) error {
if c .Args == nil {
return ArbitraryArgs (c , args )
}
return c .Args (c , args )
}
func (c *Command ) ValidateRequiredFlags () error {
if c .DisableFlagParsing {
return nil
}
flags := c .Flags ()
missingFlagNames := []string {}
flags .VisitAll (func (pflag *flag .Flag ) {
requiredAnnotation , found := pflag .Annotations [BashCompOneRequiredFlag ]
if !found {
return
}
if (requiredAnnotation [0 ] == "true" ) && !pflag .Changed {
missingFlagNames = append (missingFlagNames , pflag .Name )
}
})
if len (missingFlagNames ) > 0 {
return fmt .Errorf (`required flag(s) "%s" not set` , strings .Join (missingFlagNames , `", "` ))
}
return nil
}
func (c *Command ) checkCommandGroups () {
for _ , sub := range c .commands {
if sub .GroupID != "" && !c .ContainsGroup (sub .GroupID ) {
panic (fmt .Sprintf ("group id '%s' is not defined for subcommand '%s'" , sub .GroupID , sub .CommandPath ()))
}
sub .checkCommandGroups ()
}
}
func (c *Command ) InitDefaultHelpFlag () {
c .mergePersistentFlags ()
if c .Flags ().Lookup (helpFlagName ) == nil {
usage := "help for "
name := c .DisplayName ()
if name == "" {
usage += "this command"
} else {
usage += name
}
c .Flags ().BoolP (helpFlagName , "h" , false , usage )
_ = c .Flags ().SetAnnotation (helpFlagName , FlagSetByCobraAnnotation , []string {"true" })
}
}
func (c *Command ) InitDefaultVersionFlag () {
if c .Version == "" {
return
}
c .mergePersistentFlags ()
if c .Flags ().Lookup ("version" ) == nil {
usage := "version for "
if c .Name () == "" {
usage += "this command"
} else {
usage += c .DisplayName ()
}
if c .Flags ().ShorthandLookup ("v" ) == nil {
c .Flags ().BoolP ("version" , "v" , false , usage )
} else {
c .Flags ().Bool ("version" , false , usage )
}
_ = c .Flags ().SetAnnotation ("version" , FlagSetByCobraAnnotation , []string {"true" })
}
}
func (c *Command ) InitDefaultHelpCmd () {
if !c .HasSubCommands () {
return
}
if c .helpCommand == nil {
c .helpCommand = &Command {
Use : "help [command]" ,
Short : "Help about any command" ,
Long : `Help provides help for any command in the application.
Simply type ` + c .DisplayName () + ` help [path to command] for full details.` ,
ValidArgsFunction : func (c *Command , args []string , toComplete string ) ([]Completion , ShellCompDirective ) {
var completions []Completion
cmd , _ , e := c .Root ().Find (args )
if e != nil {
return nil , ShellCompDirectiveNoFileComp
}
if cmd == nil {
cmd = c .Root ()
}
for _ , subCmd := range cmd .Commands () {
if subCmd .IsAvailableCommand () || subCmd == cmd .helpCommand {
if strings .HasPrefix (subCmd .Name (), toComplete ) {
completions = append (completions , CompletionWithDesc (subCmd .Name (), subCmd .Short ))
}
}
}
return completions , ShellCompDirectiveNoFileComp
},
Run : func (c *Command , args []string ) {
cmd , _ , e := c .Root ().Find (args )
if cmd == nil || e != nil {
c .Printf ("Unknown help topic %#q\n" , args )
CheckErr (c .Root ().Usage ())
} else {
cmd .InitDefaultHelpFlag ()
cmd .InitDefaultVersionFlag ()
CheckErr (cmd .Help ())
}
},
GroupID : c .helpCommandGroupID ,
}
}
c .RemoveCommand (c .helpCommand )
c .AddCommand (c .helpCommand )
}
func (c *Command ) ResetCommands () {
c .parent = nil
c .commands = nil
c .helpCommand = nil
c .parentsPflags = nil
}
type commandSorterByName []*Command
func (c commandSorterByName ) Len () int { return len (c ) }
func (c commandSorterByName ) Swap (i , j int ) { c [i ], c [j ] = c [j ], c [i ] }
func (c commandSorterByName ) Less (i , j int ) bool { return c [i ].Name () < c [j ].Name () }
func (c *Command ) Commands () []*Command {
if EnableCommandSorting && !c .commandsAreSorted {
sort .Sort (commandSorterByName (c .commands ))
c .commandsAreSorted = true
}
return c .commands
}
func (c *Command ) AddCommand (cmds ...*Command ) {
for i , x := range cmds {
if cmds [i ] == c {
panic ("Command can't be a child of itself" )
}
cmds [i ].parent = c
usageLen := len (x .Use )
if usageLen > c .commandsMaxUseLen {
c .commandsMaxUseLen = usageLen
}
commandPathLen := len (x .CommandPath ())
if commandPathLen > c .commandsMaxCommandPathLen {
c .commandsMaxCommandPathLen = commandPathLen
}
nameLen := len (x .Name ())
if nameLen > c .commandsMaxNameLen {
c .commandsMaxNameLen = nameLen
}
if c .globNormFunc != nil {
x .SetGlobalNormalizationFunc (c .globNormFunc )
}
c .commands = append (c .commands , x )
c .commandsAreSorted = false
}
}
func (c *Command ) Groups () []*Group {
return c .commandgroups
}
func (c *Command ) AllChildCommandsHaveGroup () bool {
for _ , sub := range c .commands {
if (sub .IsAvailableCommand () || sub == c .helpCommand ) && sub .GroupID == "" {
return false
}
}
return true
}
func (c *Command ) ContainsGroup (groupID string ) bool {
for _ , x := range c .commandgroups {
if x .ID == groupID {
return true
}
}
return false
}
func (c *Command ) AddGroup (groups ...*Group ) {
c .commandgroups = append (c .commandgroups , groups ...)
}
func (c *Command ) RemoveCommand (cmds ...*Command ) {
commands := []*Command {}
main :
for _ , command := range c .commands {
for _ , cmd := range cmds {
if command == cmd {
command .parent = nil
continue main
}
}
commands = append (commands , command )
}
c .commands = commands
c .commandsMaxUseLen = 0
c .commandsMaxCommandPathLen = 0
c .commandsMaxNameLen = 0
for _ , command := range c .commands {
usageLen := len (command .Use )
if usageLen > c .commandsMaxUseLen {
c .commandsMaxUseLen = usageLen
}
commandPathLen := len (command .CommandPath ())
if commandPathLen > c .commandsMaxCommandPathLen {
c .commandsMaxCommandPathLen = commandPathLen
}
nameLen := len (command .Name ())
if nameLen > c .commandsMaxNameLen {
c .commandsMaxNameLen = nameLen
}
}
}
func (c *Command ) Print (i ...interface {}) {
fmt .Fprint (c .OutOrStderr (), i ...)
}
func (c *Command ) Println (i ...interface {}) {
c .Print (fmt .Sprintln (i ...))
}
func (c *Command ) Printf (format string , i ...interface {}) {
c .Print (fmt .Sprintf (format , i ...))
}
func (c *Command ) PrintErr (i ...interface {}) {
fmt .Fprint (c .ErrOrStderr (), i ...)
}
func (c *Command ) PrintErrln (i ...interface {}) {
c .PrintErr (fmt .Sprintln (i ...))
}
func (c *Command ) PrintErrf (format string , i ...interface {}) {
c .PrintErr (fmt .Sprintf (format , i ...))
}
func (c *Command ) CommandPath () string {
if c .HasParent () {
return c .Parent ().CommandPath () + " " + c .Name ()
}
return c .DisplayName ()
}
func (c *Command ) DisplayName () string {
if displayName , ok := c .Annotations [CommandDisplayNameAnnotation ]; ok {
return displayName
}
return c .Name ()
}
func (c *Command ) UseLine () string {
var useline string
use := strings .Replace (c .Use , c .Name (), c .DisplayName (), 1 )
if c .HasParent () {
useline = c .parent .CommandPath () + " " + use
} else {
useline = use
}
if c .DisableFlagsInUseLine {
return useline
}
if c .HasAvailableFlags () && !strings .Contains (useline , "[flags]" ) {
useline += " [flags]"
}
return useline
}
func (c *Command ) DebugFlags () {
c .Println ("DebugFlags called on" , c .Name ())
var debugflags func (*Command )
debugflags = func (x *Command ) {
if x .HasFlags () || x .HasPersistentFlags () {
c .Println (x .Name ())
}
if x .HasFlags () {
x .flags .VisitAll (func (f *flag .Flag ) {
if x .HasPersistentFlags () && x .persistentFlag (f .Name ) != nil {
c .Println (" -" +f .Shorthand +"," , "--" +f .Name , "[" +f .DefValue +"]" , "" , f .Value , " [LP]" )
} else {
c .Println (" -" +f .Shorthand +"," , "--" +f .Name , "[" +f .DefValue +"]" , "" , f .Value , " [L]" )
}
})
}
if x .HasPersistentFlags () {
x .pflags .VisitAll (func (f *flag .Flag ) {
if x .HasFlags () {
if x .flags .Lookup (f .Name ) == nil {
c .Println (" -" +f .Shorthand +"," , "--" +f .Name , "[" +f .DefValue +"]" , "" , f .Value , " [P]" )
}
} else {
c .Println (" -" +f .Shorthand +"," , "--" +f .Name , "[" +f .DefValue +"]" , "" , f .Value , " [P]" )
}
})
}
c .Println (x .flagErrorBuf )
if x .HasSubCommands () {
for _ , y := range x .commands {
debugflags (y )
}
}
}
debugflags (c )
}
func (c *Command ) Name () string {
name := c .Use
i := strings .Index (name , " " )
if i >= 0 {
name = name [:i ]
}
return name
}
func (c *Command ) HasAlias (s string ) bool {
for _ , a := range c .Aliases {
if commandNameMatches (a , s ) {
return true
}
}
return false
}
func (c *Command ) CalledAs () string {
if c .commandCalledAs .called {
return c .commandCalledAs .name
}
return ""
}
func (c *Command ) hasNameOrAliasPrefix (prefix string ) bool {
if strings .HasPrefix (c .Name (), prefix ) {
c .commandCalledAs .name = c .Name ()
return true
}
for _ , alias := range c .Aliases {
if strings .HasPrefix (alias , prefix ) {
c .commandCalledAs .name = alias
return true
}
}
return false
}
func (c *Command ) NameAndAliases () string {
return strings .Join (append ([]string {c .Name ()}, c .Aliases ...), ", " )
}
func (c *Command ) HasExample () bool {
return len (c .Example ) > 0
}
func (c *Command ) Runnable () bool {
return c .Run != nil || c .RunE != nil
}
func (c *Command ) HasSubCommands () bool {
return len (c .commands ) > 0
}
func (c *Command ) IsAvailableCommand () bool {
if len (c .Deprecated ) != 0 || c .Hidden {
return false
}
if c .HasParent () && c .Parent ().helpCommand == c {
return false
}
if c .Runnable () || c .HasAvailableSubCommands () {
return true
}
return false
}
func (c *Command ) IsAdditionalHelpTopicCommand () bool {
if c .Runnable () || len (c .Deprecated ) != 0 || c .Hidden {
return false
}
for _ , sub := range c .commands {
if !sub .IsAdditionalHelpTopicCommand () {
return false
}
}
return true
}
func (c *Command ) HasHelpSubCommands () bool {
for _ , sub := range c .commands {
if sub .IsAdditionalHelpTopicCommand () {
return true
}
}
return false
}
func (c *Command ) HasAvailableSubCommands () bool {
for _ , sub := range c .commands {
if sub .IsAvailableCommand () {
return true
}
}
return false
}
func (c *Command ) HasParent () bool {
return c .parent != nil
}
func (c *Command ) GlobalNormalizationFunc () func (f *flag .FlagSet , name string ) flag .NormalizedName {
return c .globNormFunc
}
func (c *Command ) Flags () *flag .FlagSet {
if c .flags == nil {
c .flags = flag .NewFlagSet (c .DisplayName (), flag .ContinueOnError )
if c .flagErrorBuf == nil {
c .flagErrorBuf = new (bytes .Buffer )
}
c .flags .SetOutput (c .flagErrorBuf )
}
return c .flags
}
func (c *Command ) LocalNonPersistentFlags () *flag .FlagSet {
persistentFlags := c .PersistentFlags ()
out := flag .NewFlagSet (c .DisplayName (), flag .ContinueOnError )
c .LocalFlags ().VisitAll (func (f *flag .Flag ) {
if persistentFlags .Lookup (f .Name ) == nil {
out .AddFlag (f )
}
})
return out
}
func (c *Command ) LocalFlags () *flag .FlagSet {
c .mergePersistentFlags ()
if c .lflags == nil {
c .lflags = flag .NewFlagSet (c .DisplayName (), flag .ContinueOnError )
if c .flagErrorBuf == nil {
c .flagErrorBuf = new (bytes .Buffer )
}
c .lflags .SetOutput (c .flagErrorBuf )
}
c .lflags .SortFlags = c .Flags ().SortFlags
if c .globNormFunc != nil {
c .lflags .SetNormalizeFunc (c .globNormFunc )
}
addToLocal := func (f *flag .Flag ) {
if c .lflags .Lookup (f .Name ) == nil && f != c .parentsPflags .Lookup (f .Name ) {
c .lflags .AddFlag (f )
}
}
c .Flags ().VisitAll (addToLocal )
c .PersistentFlags ().VisitAll (addToLocal )
return c .lflags
}
func (c *Command ) InheritedFlags () *flag .FlagSet {
c .mergePersistentFlags ()
if c .iflags == nil {
c .iflags = flag .NewFlagSet (c .DisplayName (), flag .ContinueOnError )
if c .flagErrorBuf == nil {
c .flagErrorBuf = new (bytes .Buffer )
}
c .iflags .SetOutput (c .flagErrorBuf )
}
local := c .LocalFlags ()
if c .globNormFunc != nil {
c .iflags .SetNormalizeFunc (c .globNormFunc )
}
c .parentsPflags .VisitAll (func (f *flag .Flag ) {
if c .iflags .Lookup (f .Name ) == nil && local .Lookup (f .Name ) == nil {
c .iflags .AddFlag (f )
}
})
return c .iflags
}
func (c *Command ) NonInheritedFlags () *flag .FlagSet {
return c .LocalFlags ()
}
func (c *Command ) PersistentFlags () *flag .FlagSet {
if c .pflags == nil {
c .pflags = flag .NewFlagSet (c .DisplayName (), flag .ContinueOnError )
if c .flagErrorBuf == nil {
c .flagErrorBuf = new (bytes .Buffer )
}
c .pflags .SetOutput (c .flagErrorBuf )
}
return c .pflags
}
func (c *Command ) ResetFlags () {
c .flagErrorBuf = new (bytes .Buffer )
c .flagErrorBuf .Reset ()
c .flags = flag .NewFlagSet (c .DisplayName (), flag .ContinueOnError )
c .flags .SetOutput (c .flagErrorBuf )
c .pflags = flag .NewFlagSet (c .DisplayName (), flag .ContinueOnError )
c .pflags .SetOutput (c .flagErrorBuf )
c .lflags = nil
c .iflags = nil
c .parentsPflags = nil
}
func (c *Command ) HasFlags () bool {
return c .Flags ().HasFlags ()
}
func (c *Command ) HasPersistentFlags () bool {
return c .PersistentFlags ().HasFlags ()
}
func (c *Command ) HasLocalFlags () bool {
return c .LocalFlags ().HasFlags ()
}
func (c *Command ) HasInheritedFlags () bool {
return c .InheritedFlags ().HasFlags ()
}
func (c *Command ) HasAvailableFlags () bool {
return c .Flags ().HasAvailableFlags ()
}
func (c *Command ) HasAvailablePersistentFlags () bool {
return c .PersistentFlags ().HasAvailableFlags ()
}
func (c *Command ) HasAvailableLocalFlags () bool {
return c .LocalFlags ().HasAvailableFlags ()
}
func (c *Command ) HasAvailableInheritedFlags () bool {
return c .InheritedFlags ().HasAvailableFlags ()
}
func (c *Command ) Flag (name string ) (flag *flag .Flag ) {
flag = c .Flags ().Lookup (name )
if flag == nil {
flag = c .persistentFlag (name )
}
return
}
func (c *Command ) persistentFlag (name string ) (flag *flag .Flag ) {
if c .HasPersistentFlags () {
flag = c .PersistentFlags ().Lookup (name )
}
if flag == nil {
c .updateParentsPflags ()
flag = c .parentsPflags .Lookup (name )
}
return
}
func (c *Command ) ParseFlags (args []string ) error {
if c .DisableFlagParsing {
return nil
}
if c .flagErrorBuf == nil {
c .flagErrorBuf = new (bytes .Buffer )
}
beforeErrorBufLen := c .flagErrorBuf .Len ()
c .mergePersistentFlags ()
c .Flags ().ParseErrorsWhitelist = flag .ParseErrorsWhitelist (c .FParseErrWhitelist )
err := c .Flags ().Parse (args )
if c .flagErrorBuf .Len ()-beforeErrorBufLen > 0 && err == nil {
c .Print (c .flagErrorBuf .String ())
}
return err
}
func (c *Command ) Parent () *Command {
return c .parent
}
func (c *Command ) mergePersistentFlags () {
c .updateParentsPflags ()
c .Flags ().AddFlagSet (c .PersistentFlags ())
c .Flags ().AddFlagSet (c .parentsPflags )
}
func (c *Command ) updateParentsPflags () {
if c .parentsPflags == nil {
c .parentsPflags = flag .NewFlagSet (c .DisplayName (), flag .ContinueOnError )
c .parentsPflags .SetOutput (c .flagErrorBuf )
c .parentsPflags .SortFlags = false
}
if c .globNormFunc != nil {
c .parentsPflags .SetNormalizeFunc (c .globNormFunc )
}
c .Root ().PersistentFlags ().AddFlagSet (flag .CommandLine )
c .VisitParents (func (parent *Command ) {
c .parentsPflags .AddFlagSet (parent .PersistentFlags ())
})
}
func commandNameMatches(s string , t string ) bool {
if EnableCaseInsensitive {
return strings .EqualFold (s , t )
}
return s == t
}
type tmplFunc struct {
tmpl string
fn func (io .Writer , interface {}) error
}
var defaultUsageTemplate = `Usage:{{if .Runnable}}
{{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
{{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
Aliases:
{{.NameAndAliases}}{{end}}{{if .HasExample}}
Examples:
{{.Example}}{{end}}{{if .HasAvailableSubCommands}}{{$cmds := .Commands}}{{if eq (len .Groups) 0}}
Available Commands:{{range $cmds}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{else}}{{range $group := .Groups}}
{{.Title}}{{range $cmds}}{{if (and (eq .GroupID $group.ID) (or .IsAvailableCommand (eq .Name "help")))}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if not .AllChildCommandsHaveGroup}}
Additional Commands:{{range $cmds}}{{if (and (eq .GroupID "") (or .IsAvailableCommand (eq .Name "help")))}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
Flags:
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
Global Flags:
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}
Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
`
func defaultUsageFunc(w io .Writer , in interface {}) error {
c := in .(*Command )
fmt .Fprint (w , "Usage:" )
if c .Runnable () {
fmt .Fprintf (w , "\n %s" , c .UseLine ())
}
if c .HasAvailableSubCommands () {
fmt .Fprintf (w , "\n %s [command]" , c .CommandPath ())
}
if len (c .Aliases ) > 0 {
fmt .Fprintf (w , "\n\nAliases:\n" )
fmt .Fprintf (w , " %s" , c .NameAndAliases ())
}
if c .HasExample () {
fmt .Fprintf (w , "\n\nExamples:\n" )
fmt .Fprintf (w , "%s" , c .Example )
}
if c .HasAvailableSubCommands () {
cmds := c .Commands ()
if len (c .Groups ()) == 0 {
fmt .Fprintf (w , "\n\nAvailable Commands:" )
for _ , subcmd := range cmds {
if subcmd .IsAvailableCommand () || subcmd .Name () == helpCommandName {
fmt .Fprintf (w , "\n %s %s" , rpad (subcmd .Name (), subcmd .NamePadding ()), subcmd .Short )
}
}
} else {
for _ , group := range c .Groups () {
fmt .Fprintf (w , "\n\n%s" , group .Title )
for _ , subcmd := range cmds {
if subcmd .GroupID == group .ID && (subcmd .IsAvailableCommand () || subcmd .Name () == helpCommandName ) {
fmt .Fprintf (w , "\n %s %s" , rpad (subcmd .Name (), subcmd .NamePadding ()), subcmd .Short )
}
}
}
if !c .AllChildCommandsHaveGroup () {
fmt .Fprintf (w , "\n\nAdditional Commands:" )
for _ , subcmd := range cmds {
if subcmd .GroupID == "" && (subcmd .IsAvailableCommand () || subcmd .Name () == helpCommandName ) {
fmt .Fprintf (w , "\n %s %s" , rpad (subcmd .Name (), subcmd .NamePadding ()), subcmd .Short )
}
}
}
}
}
if c .HasAvailableLocalFlags () {
fmt .Fprintf (w , "\n\nFlags:\n" )
fmt .Fprint (w , trimRightSpace (c .LocalFlags ().FlagUsages ()))
}
if c .HasAvailableInheritedFlags () {
fmt .Fprintf (w , "\n\nGlobal Flags:\n" )
fmt .Fprint (w , trimRightSpace (c .InheritedFlags ().FlagUsages ()))
}
if c .HasHelpSubCommands () {
fmt .Fprintf (w , "\n\nAdditional help topcis:" )
for _ , subcmd := range c .Commands () {
if subcmd .IsAdditionalHelpTopicCommand () {
fmt .Fprintf (w , "\n %s %s" , rpad (subcmd .CommandPath (), subcmd .CommandPathPadding ()), subcmd .Short )
}
}
}
if c .HasAvailableSubCommands () {
fmt .Fprintf (w , "\n\nUse \"%s [command] --help\" for more information about a command." , c .CommandPath ())
}
fmt .Fprintln (w )
return nil
}
var defaultHelpTemplate = `{{with (or .Long .Short)}}{{. | trimTrailingWhitespaces}}
{{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`
func defaultHelpFunc(w io .Writer , in interface {}) error {
c := in .(*Command )
usage := c .Long
if usage == "" {
usage = c .Short
}
usage = trimRightSpace (usage )
if usage != "" {
fmt .Fprintln (w , usage )
fmt .Fprintln (w )
}
if c .Runnable () || c .HasSubCommands () {
fmt .Fprint (w , c .UsageString ())
}
return nil
}
var defaultVersionTemplate = `{{with .DisplayName}}{{printf "%s " .}}{{end}}{{printf "version %s" .Version}}
`
func defaultVersionFunc(w io .Writer , in interface {}) error {
c := in .(*Command )
_ , err := fmt .Fprintf (w , "%s version %s\n" , c .DisplayName (), c .Version )
return err
}
The pages are generated with Golds v0.8.4 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .