forked from go-clix/cli
-
Notifications
You must be signed in to change notification settings - Fork 0
/
command.go
151 lines (125 loc) · 3.33 KB
/
command.go
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
package cli
import (
"fmt"
"log"
"os"
"strings"
"github.com/posener/complete"
"github.com/spf13/pflag"
)
// Command represents a (sub)command of the application. Either `Run()` must be
// defined, or subcommands added using `AddCommand()`. These are also mutually
// exclusive.
type Command struct {
// Usage line. First word must be the command name, everything else is
// displayed as-is.
Use string
// Aliases define alternative names for a command
Aliases []string
// Short help text, used for overviews
Short string
// Long help text, used for full help pages. `Short` is used as a fallback
// if unset.
Long string
// Version of the application. Only used on the root command
Version string
// Run is the action that is run when this command is invoked.
// The error is returned as-is from `Execute()`.
Run func(cmd *Command, args []string) error
// Validation + Completion
//
// Predict contains Predictors for flags. Defaults to
// `complete.PredictSomething` if unset.
// Use the flags name (not shorthand) as the key.
Predictors map[string]complete.Predictor
// Args is used to validate and complete positional arguments
Args Arguments
// internal fields
children []*Command
flags *pflag.FlagSet
parentPtr *Command
}
// Execute runs the application. It should be run on the most outer level
// command.
// The error return value is used for both, application errors but also help texts.
func (c *Command) Execute() error {
// Execute must be called on the top level command
if c.parentPtr != nil {
return c.parentPtr.Execute()
}
// add subcommand for install CLI completions
if len(c.children) != 0 {
c.AddCommand(completionCmd(c.Use))
}
// exit if in bash completion context
if predict(c) {
return nil
}
// find the correct (sub)command
command, args, err := findTarget(c, os.Args[1:])
if err != nil {
return err
}
return command.execute(args)
}
func (c *Command) execute(args []string) error {
// add help flag
var showHelp *bool
if c.Flags().Lookup("help") == nil {
showHelp = initHelpFlag(c)
}
// add version flag, but only to the root command.
var showVersion *bool
if c.parentPtr == nil && c.Version != "" {
showVersion = c.Flags().Bool("version", false, fmt.Sprintf("version for %s", c.Use))
}
// parse flags
if err := c.Flags().Parse(args); err != nil {
return c.help(err)
}
// show version if requested.
if showVersion != nil && *showVersion {
log.Printf("%s version %s", c.Use, c.Version)
return nil
}
// show help if requested or missing `Run()`
switch {
case showHelp != nil && *showHelp:
fallthrough
case c.Run == nil:
return helpErr(c)
}
// validate args
if c.Args == nil {
c.Args = ArgsAny()
}
if err := c.Args.Validate(c.Flags().Args()); err != nil {
return c.help(err)
}
// run!
return c.Run(c, c.Flags().Args())
}
func initHelpFlag(c *Command) *bool {
return c.Flags().BoolP("help", "h", false, "help for "+c.Name())
}
func helpErr(c *Command) error {
help := c.Short
if c.Long != "" {
help = c.Long
}
return ErrHelp{
Message: help,
usage: c.Usage(),
}
}
// Name of this command. The first segment of the `Use` field.
func (c *Command) Name() string {
return strings.Split(c.Use, " ")[0]
}
// Usage string
func (c *Command) Usage() string {
return c.helpable().Generate()
}
func (c *Command) helpable() *helpable {
return &helpable{*c}
}