-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathcli.go
More file actions
169 lines (134 loc) · 3.71 KB
/
cli.go
File metadata and controls
169 lines (134 loc) · 3.71 KB
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
package cli
import (
"fmt"
"net/http"
"os"
"reflect"
"runtime"
"github.com/jessevdk/go-flags"
)
type DeferFunc func()
// App defines the CLI application that will be run.
type App struct {
Parser *flags.Parser
// DebugServeMux is serves debug endpoints. It used to attach the http/pprof
// endpoint if enabled, and can be used to handle other debug endpoints.
DebugServeMux *http.ServeMux
// deferFuncs holds the functions to be called when the command finishes.
deferFuncs []DeferFunc
}
// New creates a new App, including default values and sub commands.
func New(name, version, build, description string) *App {
app := NewNoDefaults(name, description)
app.AddCommand(&VersionCommand{
Name: name,
Version: version,
Build: build,
})
if runtime.GOOS != "windows" {
app.AddCommand(&CompletionCommand{
Name: name,
}, InitCompletionCommand(name))
}
return app
}
// NewNoDefaults creates a new App, without any of the default sub commands.
func NewNoDefaults(name, description string) *App {
parser := flags.NewNamedParser(name, flags.Default)
parser.LongDescription = description
app := &App{
Parser: parser,
DebugServeMux: http.NewServeMux(),
}
app.Parser.CommandHandler = app.commandHandler
return app
}
// Run runs the app with the given command line arguments. In order to reduce
// boilerplate, RunMain should be used instead.
func (a *App) Run(args []string) error {
defer a.callDefer()
if _, err := a.Parser.ParseArgs(args[1:]); err != nil {
if err, ok := err.(*flags.Error); ok {
if err.Type == flags.ErrHelp {
return nil
}
a.Parser.WriteHelp(os.Stderr)
}
return err
}
return nil
}
// RunMain runs the application with os.Args and if there is any error, it
// exits with error code 1.
func (a *App) RunMain() {
if err := a.Run(os.Args); err != nil {
os.Exit(1)
}
}
// Defer adds a function to be called after the command is executed. The
// functions added are called in reverse order.
func (a *App) Defer(d DeferFunc) {
a.deferFuncs = append(a.deferFuncs, d)
}
func (a *App) callDefer() {
for i := len(a.deferFuncs) - 1; i >= 0; i-- {
f := a.deferFuncs[i]
if f != nil {
f()
}
}
}
func (a *App) commandHandler(cmd flags.Commander, args []string) error {
if v, ok := cmd.(Initializer); ok {
if err := v.Init(a); err != nil {
return err
}
}
if v, ok := cmd.(ContextCommander); ok {
return executeContextCommander(v, args)
}
return cmd.Execute(args)
}
func getStructType(data interface{}) (reflect.Type, error) {
typ := reflect.TypeOf(data)
if typ == nil {
return nil, fmt.Errorf("expected struct or struct ptr: got nil")
}
for typ.Kind() == reflect.Ptr {
typ = typ.Elem()
}
if typ.Kind() != reflect.Struct {
return nil, fmt.Errorf("expected struct or struct ptr: %s", typ.Kind())
}
return typ, nil
}
// Initializer interface provides an Init function.
type Initializer interface {
// Init initializes the command.
Init(*App) error
}
// PlainCommand should be embedded in a struct to indicate that it implements a
// command. See package documentation for its usage.
type PlainCommand struct{}
// Execute is a placeholder for the function that runs the command.
func (c PlainCommand) Execute(args []string) error {
return nil
}
// Command implements the default group flags. It is meant to be embedded into
// other application commands to provide default behavior for logging,
// profiling, etc.
type Command struct {
PlainCommand
LogOptions `group:"Log Options"`
ProfilerOptions `group:"Profiler Options"`
}
// Init implements initializer interface.
func (c Command) Init(a *App) error {
if err := c.LogOptions.Init(a); err != nil {
return err
}
if err := c.ProfilerOptions.Init(a); err != nil {
return err
}
return nil
}