-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcommand.go
More file actions
258 lines (218 loc) · 8.57 KB
/
command.go
File metadata and controls
258 lines (218 loc) · 8.57 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
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
package cmder
import (
"context"
"flag"
)
// Command is the fundamental interface implemented by types that are runnable commands or subcommands. Commands can
// be executed with [Execute].
//
// Concrete types can implement additional interfaces to configure additional behaviour, like setup/teardown routines,
// subcommands, command-line flags, and other behaviour:
//
// - If you want to configure setup and teardown routines for a command, see [Initializer] and [Destroyer].
// - If your command has subcommands, see [RootCommand].
// - If your command has command-life flags and switches, see [FlagInitializer].
type Command interface {
// All commands are [Runnable] and implement a Run routine.
Runnable
// Great tools come with great documentation. All commands need to provide documentation, which is used when
// rendering usage and help texts.
Documented
// Name returns the name of this command or subcommand.
Name() string
}
// Runnable is a fundamental interface implemented by commands. The Run routine is what carries out the work of your
// command.
//
// Concrete types can also implement [Initializer] or [Destroyer] to carry out any initialization and teardown
// necessary for your Run() routine.
type Runnable interface {
// Run is the main body of your command executed by [Execute].
//
// The given [context.Context] is derived from the context provided to Execute() and is cancelled when Execute()
// returns. Use this context to cleanup resources.
//
// The second argument is the list of command-line arguments and switches that remain after parsing flags.
Run(context.Context, []string) error
}
// Initializer may be implemented by commands that need to do some work before the [Runnable] Run() routine is invoked.
//
// See [Execute] for more details on the lifecycle of command execution.
type Initializer interface {
// Initialize carries out any initialization needed for this [Command]. Errors returned by Initialize will abort
// execution of the command lifecycle (Run()/Destroy() of this command and parent command(s)).
Initialize(context.Context, []string) error
}
// Destroyer may be implemented by commands that need to do some work after the [Runnable] Run() routine is invoked.
//
// See [Execute] for more details on the lifecycle of command execution.
type Destroyer interface {
// Destroy carries out any teardown needed for this [Command]. Errors returned by Destroy will abort execution of
// the command lifecycle (Destroy of this command and parent command(s)).
Destroy(context.Context, []string) error
}
// RootCommand may be implemented by commands that have subcommands.
type RootCommand interface {
// Subcommands returns a slice of subcommands of this RootCommand. May return nil or an empty slice to treat this
// command as a leaf command.
Subcommands() []Command
}
// Documented is implemented by all commands and provides help and usage information for your users.
type Documented interface {
// UsageLine returns the usage line for your command. This is akin to the "SYNOPSIS" section you would typically
// find in a man page. Generally, usage lines have a well accepted format:
//
// - [ ] identifies an optional argument or flag. Arguments that are not enclosed in brackets are required.
// - ... identifies arguments or flags that can be provided more than once.
// - | identifies mutually exclusive arguments or flags.
// - ( ) identifies groups of flags or arguments that are required together.
// - < > identifies argument(s) or flag(s).
//
// Here are a few examples:
//
// git add [<options>] [--] <pathspec>...
// kubectl get [(-o|--output=)json|yaml|wide] (TYPE[.VERSION][.GROUP] [NAME | -l label] | TYPE[.VERSION][.GROUP]/NAME ...) [flags] [options]
// crane index filter [flags]
UsageLine() string
// ShortHelpText returns a short-and-sweet one-line description of your command. This is akin to the "NAME" section
// you would typically find in a man page. This is mainly used to summarize available subcommands.
//
// Here are a few examples:
//
// get image registry and namespace information
// download installation files for an application version
// display the contents of a file in a terminal
ShortHelpText() string
// HelpText returns longer usage and help information for your users about this subcommand. Here you can describe
// the behaviour of your command, summarize usage of certain flags and arguments and provide hints on where to find
// additional information. This is akin to the "DESCRIPTION" section you would typically find in a man page.
//
// For a better viewing experience in terminals, consider maintaining consistent line length limits (120 is a good
// target).
HelpText() string
// ExampleText returns motivating usage examples for your command.
ExampleText() string
}
// HiddenCommand is implemented by commands which are not user facing. Hidden commands are not displayed in help texts.
type HiddenCommand interface {
// Hidden returns a flag indicating whether to mark this command as hidden, preventing it from being rendered in
// help output.
Hidden() bool
}
// Compile-time checks.
var (
_ Command = &BaseCommand{}
_ Initializer = &BaseCommand{}
_ Destroyer = &BaseCommand{}
_ RootCommand = &BaseCommand{}
_ FlagInitializer = &BaseCommand{}
_ Documented = &CommandDocumentation{}
_ HiddenCommand = &CommandDocumentation{}
)
// CommandDocumentation implements [Documented] and can be embdded in command types to reduce boilerplate.
type CommandDocumentation struct {
// The usage line. See UsageLine() in [Documented].
Usage string
// The short help line. See ShortHelpText() in [Documented].
ShortHelp string
// Documentation for your command. See HelpText() in [Documented].
Help string
// Usage examples for your command. See ExampleText() in [Documented].
Examples string
// Whether this command is hidden in help and usage texts. See Hidden() in [HiddenCommand].
IsHidden bool
}
// UsageLine returns [CommandDocumentation] Usage.
//
// See [Documented].
func (d CommandDocumentation) UsageLine() string {
return d.Usage
}
// ShortHelpText returns [CommandDocumentation] ShortHelp.
//
// See [Documented].
func (d CommandDocumentation) ShortHelpText() string {
return d.ShortHelp
}
// HelpText returns [CommandDocumentation] Help.
//
// See [Documented].
func (d CommandDocumentation) HelpText() string {
return d.Help
}
// ExampleText returns [CommandDocumentation] Examples.
//
// See [Documented].
func (d CommandDocumentation) ExampleText() string {
return d.Examples
}
// Hidden returns [CommandDocumentation] Hidden.
//
// See [HiddenCommand].
func (d CommandDocumentation) Hidden() bool {
return d.IsHidden
}
// BaseCommand is an implementation of the [Command], [Initializer], [Destroyer], [RootCommand] and [FlagInitializer]
// interfaces and may be embedded in your command types to reduce boilerplate.
type BaseCommand struct {
CommandDocumentation
// The command name. See Name() in [Command].
CommandName string
// Optional function invoked by the default InitializeFlags() function.
InitFlagsFunc func(*flag.FlagSet)
// Optional function invoked by the default Initialize() function.
InitFunc func(context.Context, []string) error
// Optional function invoked by the default Run() function.
RunFunc func(context.Context, []string) error
// Optional function invoked by the default Destroy() function.
DestroyFunc func(context.Context, []string) error
// Subcommands for this command, if applicable. See [RootCommand].
Children []Command
}
// Name returns [BaseCommand] CommandName.
//
// See [Command].
func (c BaseCommand) Name() string {
return c.CommandName
}
// InitializeFlags runs [BaseCommand] InitFlagsFunc, if not nil.
//
// See [FlagInitializer].
func (c BaseCommand) InitializeFlags(fs *flag.FlagSet) {
if c.InitFlagsFunc != nil {
c.InitFlagsFunc(fs)
}
}
// Initialize runs [BaseCommand] InitFunc, if not nil.
//
// See [Initializer].
func (c BaseCommand) Initialize(ctx context.Context, args []string) error {
if c.InitFunc != nil {
return c.InitFunc(ctx, args)
}
return nil
}
// Run runs [BaseCommand] RunFunc, if not nil.
//
// See [Runnable].
func (c BaseCommand) Run(ctx context.Context, args []string) error {
if c.RunFunc != nil {
return c.RunFunc(ctx, args)
}
return nil
}
// Destroy runs [BaseCommand] DestroyFunc, if not nil.
//
// See [Destroyer].
func (c BaseCommand) Destroy(ctx context.Context, args []string) error {
if c.DestroyFunc != nil {
return c.DestroyFunc(ctx, args)
}
return nil
}
// Subcommands returns [BaseCommand] Children.
//
// See [RootCommand].
func (c BaseCommand) Subcommands() []Command {
return c.Children
}