aboutsummaryrefslogtreecommitdiffstats
path: root/Godeps/_workspace/src/github.com/codegangsta/cli/app.go
blob: 1ea3fd0b17df8b8ef187ffc7e85e1c1ae2b2611f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
package cli

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

// 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
    // 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
    HideVersion bool
    // An action to execute when the bash-completion flag is set
    BashComplete func(context *Context)
    // 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 func(context *Context) error
    // An action to execute after any subcommands are run, but after the subcommand has finished
    // It is run even if Action() panics
    After func(context *Context) error
    // The action to execute when no subcommands are specified
    Action func(context *Context)
    // Execute this function if the proper command cannot be found
    CommandNotFound func(context *Context, command string)
    // Execute this function, if an usage error occurs. This is useful for displaying customized usage error messages.
    // This function is able to replace the original error messages.
    // If this function is not set, the "Incorrect usage" is displayed and the execution is interrupted.
    OnUsageError func(context *Context, err error, isSubcommand bool) error
    // 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
}

// 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()
}

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

// 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) {
    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

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

    //append version/help flags
    if a.EnableBashCompletion {
        a.appendFlag(BashCompletionFlag)
    }

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

    // parse flags
    set := flagSet(a.Name, a.Flags)
    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
    }

    if checkCompletions(context) {
        return nil
    }

    if err != nil {
        if a.OnUsageError != nil {
            err := a.OnUsageError(context, err, false)
            return err
        } else {
            fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.")
            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 {
        err = a.Before(context)
        if err != nil {
            fmt.Fprintf(a.Writer, "%v\n\n", err)
            ShowAppHelp(context)
            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
    a.Action(context)
    return nil
}

// Another entry point to the cli app, takes care of passing arguments and error handling
func (a *App) RunAndExitOnError() {
    if err := a.Run(os.Args); err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
}

// 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

    // append flags
    if a.EnableBashCompletion {
        a.appendFlag(BashCompletionFlag)
    }

    // parse flags
    set := flagSet(a.Name, a.Flags)
    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)
            return err
        } else {
            fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.")
            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 {
                if err != nil {
                    err = NewMultiError(err, afterErr)
                } else {
                    err = afterErr
                }
            }
        }()
    }

    if a.Before != nil {
        err := a.Before(context)
        if err != nil {
            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
    a.Action(context)

    return nil
}

// 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
}

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

    return false
}

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)
}