aboutsummaryrefslogblamecommitdiffstats
path: root/vendor/gopkg.in/urfave/cli.v1/app.go
blob: 51fc45d87815ec7336bedbe6a9e0525e57636650 (plain) (tree)
1
2
3
4
5
6
7
8
9






                   
                       
              


              
     
                                                                                                  




                                                                                                                                        

                                                                                                         


                                                                  
                                                                         
                                                   
                 
                                                                     
                   

                                                          

                                      

                                                     

                                                      

                                 

                                     







                                                     
                                                                                
                        

                                                                             
                                                                    
                                     

                                                                                              
                         

                                                                                                    
                       
 
                                                                  

                                                                                                           
                          
 
                                                                      


                                                         

                           

                                              

                                         
                                                                     
                     
                                                                      


                                           



                                        





                                                                      

                     











                                                   

                                                                               

                    

                                                        
                                                      
                                 



                                                 



                                        









                                                                             

                                                                                     

         



                                                                             
                 
                                            
         
                            
 






                                                              


                                         





                                                                                 







                                                         





                                                                               
 







                                                                                   
                      




                                            


                                            
                                          

                                            
                                    

                           
                                             




                                      


                                                                  
                                            

                                  
                                                                                   

                                    

         

                                              


                          

                                                    




                              
                                                                          





                                                                          



                            

                                              
                                            

                                                  












                                             



                                             
                             



                                             

 




                                                                              

                                              

                                                


         

                                                                                 










                                                                      








                                                                             



                                            

         


                                            
                                          


                                            
                                      




                                                                    


                           



                                      


                                                                
                                            

                                  
                                                                                   

                                           

         











                                                                  

                                                    
                                                    





                                                                          



                            



                                                  













                                             
                                             
 

                            

 
                                                                                      









                                             







































                                                                                          









                                       









                                                                   




                                               










                                                                                                         
                                        

         
                                             
 
 


                                                                              
                                                                     





                                                                                         
                          

                                           
         
 
package cli

import (
    "fmt"
    "io"
    "io/ioutil"
    "os"
    "path/filepath"
    "sort"
    "time"
)

var (
    changeLogURL                    = "https://github.com/urfave/cli/blob/master/CHANGELOG.md"
    appActionDeprecationURL         = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL)
    runAndExitOnErrorDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-runandexitonerror", changeLogURL)

    contactSysadmin = "This is an error in the application.  Please contact the distributor of this application if this is not you."

    errInvalidActionType = NewExitError("ERROR invalid Action type. "+
        fmt.Sprintf("Must be `func(*Context`)` or `func(*Context) error).  %s", contactSysadmin)+
        fmt.Sprintf("See %s", appActionDeprecationURL), 2)
)

// App is the main structure of a cli application. It is recommended that
// an app be created with the cli.NewApp() function
type App struct {
    // The name of the program. Defaults to path.Base(os.Args[0])
    Name string
    // Full name of command for help, defaults to Name
    HelpName string
    // Description of the program.
    Usage string
    // Text to override the USAGE section of help
    UsageText string
    // Description of the program argument format.
    ArgsUsage string
    // Version of the program
    Version string
    // Description of the program
    Description string
    // List of commands to execute
    Commands []Command
    // List of flags to parse
    Flags []Flag
    // Boolean to enable bash completion commands
    EnableBashCompletion bool
    // Boolean to hide built-in help command
    HideHelp bool
    // Boolean to hide built-in version flag and the VERSION section of help
    HideVersion bool
    // Populate on app startup, only gettable through method Categories()
    categories CommandCategories
    // An action to execute when the bash-completion flag is set
    BashComplete BashCompleteFunc
    // An action to execute before any subcommands are run, but after the context is ready
    // If a non-nil error is returned, no subcommands are run
    Before BeforeFunc
    // An action to execute after any subcommands are run, but after the subcommand has finished
    // It is run even if Action() panics
    After AfterFunc

    // The action to execute when no subcommands are specified
    // Expects a `cli.ActionFunc` but will accept the *deprecated* signature of `func(*cli.Context) {}`
    // *Note*: support for the deprecated `Action` signature will be removed in a future version
    Action interface{}

    // Execute this function if the proper command cannot be found
    CommandNotFound CommandNotFoundFunc
    // Execute this function if an usage error occurs
    OnUsageError OnUsageErrorFunc
    // Compilation date
    Compiled time.Time
    // List of all authors who contributed
    Authors []Author
    // Copyright of the binary if any
    Copyright string
    // Name of Author (Note: Use App.Authors, this is deprecated)
    Author string
    // Email of Author (Note: Use App.Authors, this is deprecated)
    Email string
    // Writer writer to write output to
    Writer io.Writer
    // ErrWriter writes error output
    ErrWriter io.Writer
    // Other custom info
    Metadata map[string]interface{}
    // Carries a function which returns app specific info.
    ExtraInfo func() map[string]string
    // CustomAppHelpTemplate the text template for app help topic.
    // cli.go uses text/template to render templates. You can
    // render custom help text by setting this variable.
    CustomAppHelpTemplate string

    didSetup bool
}

// Tries to find out when this binary was compiled.
// Returns the current time if it fails to find it.
func compileTime() time.Time {
    info, err := os.Stat(os.Args[0])
    if err != nil {
        return time.Now()
    }
    return info.ModTime()
}

// NewApp creates a new cli Application with some reasonable defaults for Name,
// Usage, Version and Action.
func NewApp() *App {
    return &App{
        Name:         filepath.Base(os.Args[0]),
        HelpName:     filepath.Base(os.Args[0]),
        Usage:        "A new cli application",
        UsageText:    "",
        Version:      "0.0.0",
        BashComplete: DefaultAppComplete,
        Action:       helpCommand.Action,
        Compiled:     compileTime(),
        Writer:       os.Stdout,
    }
}

// Setup runs initialization code to ensure all data structures are ready for
// `Run` or inspection prior to `Run`.  It is internally called by `Run`, but
// will return early if setup has already happened.
func (a *App) Setup() {
    if a.didSetup {
        return
    }

    a.didSetup = true

    if a.Author != "" || a.Email != "" {
        a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email})
    }

    newCmds := []Command{}
    for _, c := range a.Commands {
        if c.HelpName == "" {
            c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
        }
        newCmds = append(newCmds, c)
    }
    a.Commands = newCmds

    if a.Command(helpCommand.Name) == nil && !a.HideHelp {
        a.Commands = append(a.Commands, helpCommand)
        if (HelpFlag != BoolFlag{}) {
            a.appendFlag(HelpFlag)
        }
    }

    if !a.HideVersion {
        a.appendFlag(VersionFlag)
    }

    a.categories = CommandCategories{}
    for _, command := range a.Commands {
        a.categories = a.categories.AddCommand(command.Category, command)
    }
    sort.Sort(a.categories)

    if a.Metadata == nil {
        a.Metadata = make(map[string]interface{})
    }

    if a.Writer == nil {
        a.Writer = os.Stdout
    }
}

// Run is the entry point to the cli app. Parses the arguments slice and routes
// to the proper flag/args combination
func (a *App) Run(arguments []string) (err error) {
    a.Setup()

    // handle the completion flag separately from the flagset since
    // completion could be attempted after a flag, but before its value was put
    // on the command line. this causes the flagset to interpret the completion
    // flag name as the value of the flag before it which is undesirable
    // note that we can only do this because the shell autocomplete function
    // always appends the completion flag at the end of the command
    shellComplete, arguments := checkShellCompleteFlag(a, arguments)

    // parse flags
    set, err := flagSet(a.Name, a.Flags)
    if err != nil {
        return err
    }

    set.SetOutput(ioutil.Discard)
    err = set.Parse(arguments[1:])
    nerr := normalizeFlags(a.Flags, set)
    context := NewContext(a, set, nil)
    if nerr != nil {
        fmt.Fprintln(a.Writer, nerr)
        ShowAppHelp(context)
        return nerr
    }
    context.shellComplete = shellComplete

    if checkCompletions(context) {
        return nil
    }

    if err != nil {
        if a.OnUsageError != nil {
            err := a.OnUsageError(context, err, false)
            HandleExitCoder(err)
            return err
        }
        fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
        ShowAppHelp(context)
        return err
    }

    if !a.HideHelp && checkHelp(context) {
        ShowAppHelp(context)
        return nil
    }

    if !a.HideVersion && checkVersion(context) {
        ShowVersion(context)
        return nil
    }

    if a.After != nil {
        defer func() {
            if afterErr := a.After(context); afterErr != nil {
                if err != nil {
                    err = NewMultiError(err, afterErr)
                } else {
                    err = afterErr
                }
            }
        }()
    }

    if a.Before != nil {
        beforeErr := a.Before(context)
        if beforeErr != nil {
            ShowAppHelp(context)
            HandleExitCoder(beforeErr)
            err = beforeErr
            return err
        }
    }

    args := context.Args()
    if args.Present() {
        name := args.First()
        c := a.Command(name)
        if c != nil {
            return c.Run(context)
        }
    }

    if a.Action == nil {
        a.Action = helpCommand.Action
    }

    // Run default Action
    err = HandleAction(a.Action, context)

    HandleExitCoder(err)
    return err
}

// RunAndExitOnError calls .Run() and exits non-zero if an error was returned
//
// Deprecated: instead you should return an error that fulfills cli.ExitCoder
// to cli.App.Run. This will cause the application to exit with the given eror
// code in the cli.ExitCoder
func (a *App) RunAndExitOnError() {
    if err := a.Run(os.Args); err != nil {
        fmt.Fprintln(a.errWriter(), err)
        OsExiter(1)
    }
}

// RunAsSubcommand invokes the subcommand given the context, parses ctx.Args() to
// generate command-specific flags
func (a *App) RunAsSubcommand(ctx *Context) (err error) {
    // append help to commands
    if len(a.Commands) > 0 {
        if a.Command(helpCommand.Name) == nil && !a.HideHelp {
            a.Commands = append(a.Commands, helpCommand)
            if (HelpFlag != BoolFlag{}) {
                a.appendFlag(HelpFlag)
            }
        }
    }

    newCmds := []Command{}
    for _, c := range a.Commands {
        if c.HelpName == "" {
            c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
        }
        newCmds = append(newCmds, c)
    }
    a.Commands = newCmds

    // parse flags
    set, err := flagSet(a.Name, a.Flags)
    if err != nil {
        return err
    }

    set.SetOutput(ioutil.Discard)
    err = set.Parse(ctx.Args().Tail())
    nerr := normalizeFlags(a.Flags, set)
    context := NewContext(a, set, ctx)

    if nerr != nil {
        fmt.Fprintln(a.Writer, nerr)
        fmt.Fprintln(a.Writer)
        if len(a.Commands) > 0 {
            ShowSubcommandHelp(context)
        } else {
            ShowCommandHelp(ctx, context.Args().First())
        }
        return nerr
    }

    if checkCompletions(context) {
        return nil
    }

    if err != nil {
        if a.OnUsageError != nil {
            err = a.OnUsageError(context, err, true)
            HandleExitCoder(err)
            return err
        }
        fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
        ShowSubcommandHelp(context)
        return err
    }

    if len(a.Commands) > 0 {
        if checkSubcommandHelp(context) {
            return nil
        }
    } else {
        if checkCommandHelp(ctx, context.Args().First()) {
            return nil
        }
    }

    if a.After != nil {
        defer func() {
            afterErr := a.After(context)
            if afterErr != nil {
                HandleExitCoder(err)
                if err != nil {
                    err = NewMultiError(err, afterErr)
                } else {
                    err = afterErr
                }
            }
        }()
    }

    if a.Before != nil {
        beforeErr := a.Before(context)
        if beforeErr != nil {
            HandleExitCoder(beforeErr)
            err = beforeErr
            return err
        }
    }

    args := context.Args()
    if args.Present() {
        name := args.First()
        c := a.Command(name)
        if c != nil {
            return c.Run(context)
        }
    }

    // Run default Action
    err = HandleAction(a.Action, context)

    HandleExitCoder(err)
    return err
}

// Command returns the named command on App. Returns nil if the command does not exist
func (a *App) Command(name string) *Command {
    for _, c := range a.Commands {
        if c.HasName(name) {
            return &c
        }
    }

    return nil
}

// Categories returns a slice containing all the categories with the commands they contain
func (a *App) Categories() CommandCategories {
    return a.categories
}

// VisibleCategories returns a slice of categories and commands that are
// Hidden=false
func (a *App) VisibleCategories() []*CommandCategory {
    ret := []*CommandCategory{}
    for _, category := range a.categories {
        if visible := func() *CommandCategory {
            for _, command := range category.Commands {
                if !command.Hidden {
                    return category
                }
            }
            return nil
        }(); visible != nil {
            ret = append(ret, visible)
        }
    }
    return ret
}

// VisibleCommands returns a slice of the Commands with Hidden=false
func (a *App) VisibleCommands() []Command {
    ret := []Command{}
    for _, command := range a.Commands {
        if !command.Hidden {
            ret = append(ret, command)
        }
    }
    return ret
}

// VisibleFlags returns a slice of the Flags with Hidden=false
func (a *App) VisibleFlags() []Flag {
    return visibleFlags(a.Flags)
}

func (a *App) hasFlag(flag Flag) bool {
    for _, f := range a.Flags {
        if flag == f {
            return true
        }
    }

    return false
}

func (a *App) errWriter() io.Writer {

    // When the app ErrWriter is nil use the package level one.
    if a.ErrWriter == nil {
        return ErrWriter
    }

    return a.ErrWriter
}

func (a *App) appendFlag(flag Flag) {
    if !a.hasFlag(flag) {
        a.Flags = append(a.Flags, flag)
    }
}

// Author represents someone who has contributed to a cli project.
type Author struct {
    Name  string // The Authors name
    Email string // The Authors email
}

// String makes Author comply to the Stringer interface, to allow an easy print in the templating process
func (a Author) String() string {
    e := ""
    if a.Email != "" {
        e = " <" + a.Email + ">"
    }

    return fmt.Sprintf("%v%v", a.Name, e)
}

// HandleAction attempts to figure out which Action signature was used.  If
// it's an ActionFunc or a func with the legacy signature for Action, the func
// is run!
func HandleAction(action interface{}, context *Context) (err error) {
    if a, ok := action.(ActionFunc); ok {
        return a(context)
    } else if a, ok := action.(func(*Context) error); ok {
        return a(context)
    } else if a, ok := action.(func(*Context)); ok { // deprecated function signature
        a(context)
        return nil
    } else {
        return errInvalidActionType
    }
}