-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathoptions.go
More file actions
150 lines (126 loc) · 5.75 KB
/
options.go
File metadata and controls
150 lines (126 loc) · 5.75 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
package exoskeleton
import (
"text/template"
"github.com/square/exoskeleton/v2/pkg/shellcomp"
)
// An Option applies optional changes to an Exoskeleton Entrypoint.
type Option interface {
Apply(*Entrypoint)
}
// optionFunc is a function that adheres to the Option interface.
type optionFunc func(*Entrypoint)
// ExecFunc is called when an built-in command is run.
type ExecFunc func(e *Entrypoint, args, env []string) error
// CompleteFunc is called when an built-in command is asked to supply shell completions.
type CompleteFunc func(e *Entrypoint, args, env []string) ([]string, shellcomp.Directive, error)
// EmbeddedCommand defines a built-in command that can be added to an Entrypoint
// (as opposed to an executable external to the Entrypoint).
type EmbeddedCommand struct {
Name string
Summary string
Help string
Exec ExecFunc
Complete CompleteFunc
Commands []*EmbeddedCommand
DefaultCommand string
}
// Apply invokes the optionFunc with the given Entrypoint.
func (fn optionFunc) Apply(e *Entrypoint) {
fn(e)
}
// AppendCommands adds new embedded commands to the Entrypoint. The commands are added to
// the end of the list and will have the lowest precedence: an executable with the same name
// as one of these commands would override it.
func AppendCommands(cmds ...*EmbeddedCommand) Option {
return (optionFunc)(func(e *Entrypoint) {
e.cmdsToAppend = append(e.cmdsToAppend, buildCommands(e, cmds)...)
})
}
// PrependCommands adds new embedded commands to the Entrypoint. The command are added to
// the front of the list and will have the highest precedence: an executable with the same name
// as one of these commands would be overridden by it.
func PrependCommands(cmds ...*EmbeddedCommand) Option {
return (optionFunc)(func(e *Entrypoint) {
e.cmdsToPrepend = append(buildCommands(e, cmds), e.cmdsToPrepend...)
})
}
func buildCommands(parent Command, cmds []*EmbeddedCommand) []Command {
var result []Command
for _, cmd := range cmds {
bc := &builtinCommand{parent: parent, definition: cmd}
if len(cmd.Commands) > 0 {
bc.subcommands = buildCommands(bc, cmd.Commands)
}
result = append(result, bc)
}
return result
}
// OnError registers a callback (ErrorFunc) to be invoked when a nonfatal error occurs.
//
// These are recoverable errors such as
// - a broken symlink is encountered in one of the paths being searched
// - a command exits unnsuccessfully when invoked with --summary or --help
func OnError(fn ErrorFunc) Option {
return (optionFunc)(func(e *Entrypoint) { e.errorCallbacks = append(e.errorCallbacks, fn) })
}
// OnCommandNotFound registers a callback (CommandNotFoundFunc) to be invoked when
// the command a user attempted to execute is not found. The callback is also invoked when
// the user asks for help on a command that can not be found.
func OnCommandNotFound(fn CommandNotFoundFunc) Option {
return (optionFunc)(func(e *Entrypoint) { e.commandNotFoundCallbacks = append(e.commandNotFoundCallbacks, fn) })
}
// AfterIdentify registers a callback (AfterIdentifyFunc) to be invoked with any command
// that is successfully identified.
func AfterIdentify(fn AfterIdentifyFunc) Option {
return (optionFunc)(func(e *Entrypoint) { e.afterIdentifyCallbacks = append(e.afterIdentifyCallbacks, fn) })
}
// WithName sets the name of the entrypoint.
// (By default, this is the basename of the executable.)
func WithName(value string) Option {
return (optionFunc)(func(e *Entrypoint) { e.name = value })
}
// WithMaxDepth sets the maximum depth of the command tree.
//
// A value of 0 prohibits any submodules. All subcommands are leaves of the Entrypoint.
// (i.e. If the Go CLI were an exoskeleton, 'go doc' would be allowed, 'go mod tidy' would not.)
//
// A value of -1 (the default value) means there is no maximum depth.
func WithMaxDepth(value int) Option {
return (optionFunc)(func(e *Entrypoint) { e.maxDepth = value })
}
// WithMenuHeadingFor allows you to supply a function that determines the heading
// a Command should be listed under in the main menu.
func WithMenuHeadingFor(fn MenuHeadingForFunc) Option {
return (optionFunc)(func(e *Entrypoint) { e.menuHeadingFor = fn })
}
// WithMenuTemplate sets the template that will be used to render help for modules.
// The template will be executed with an instance of exoskeleton.Menu as its data.
func WithMenuTemplate(value *template.Template) Option {
return (optionFunc)(func(e *Entrypoint) { e.menuTemplate = value })
}
// WithExecutor supplies a function that executes a subcommand.
// The default executor calls `Run()` on the command and returns the error.
func WithExecutor(value ExecutorFunc) Option {
return (optionFunc)(func(e *Entrypoint) { e.executor = value })
}
// WithModuleMetadataFilename sets the filename to use for module metadata.
// (Default: ".exoskeleton")
func WithModuleMetadataFilename(value string) Option {
return (optionFunc)(func(e *Entrypoint) { e.moduleMetadataFilename = value })
}
// WithContracts sets the contracts used during discovery.
// Contracts are tried in order; the first that doesn't return ErrNotApplicable builds the command.
//
// The default contracts are:
// 1. DirectoryContract (directories that contain the module metadata file)
// 2. ExecutableContract (executables with .exoskeleton extension which must implement --describe-commands)
// 3. ShellScriptContract (shell scripts with magic comments)
// 4. StandaloneExecutableContract (all other executables which must implement --summary)
func WithContracts(contracts ...Contract) Option {
return (optionFunc)(func(e *Entrypoint) { e.contracts = contracts })
}
// WithCache sets a cache for expensive operations like command execution.
// If not set, no caching is performed.
func WithCache(c Cache) Option {
return (optionFunc)(func(e *Entrypoint) { e.cache = c })
}