-
Notifications
You must be signed in to change notification settings - Fork 38
/
defaults.go
244 lines (213 loc) · 7.06 KB
/
defaults.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
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
package kflags
import (
"flag"
"fmt"
"github.com/enfabrica/enkit/lib/multierror"
"path/filepath"
"strings"
)
// Flag represents a command line flag.
type Flag interface {
// Returns the name of the flag.
Name() string
// Sets the value of the flag.
Set(string) error
// Sets the content of the flag (for those flags that support it,
// see the description of the ContentValue interface for more details)
SetContent(string, []byte) error
}
// GoFlagSet wraps a flag.FlagSet from the go standard library and completes the
// implementation of the FlagSet interface in this module.
//
// For example, to use the default "flag" library FlagSet:
//
// var set kflags.FlagSet
// set = &kflags.GoFlagSet{FlagSet: flag.CommandLine}
type GoFlagSet struct {
*flag.FlagSet
}
func (fs *GoFlagSet) ByteFileVar(p *[]byte, name string, defaultFile string, usage string, mods ...ByteFileModifier) {
fs.Var(NewByteFileFlag(p, defaultFile, mods...), name, usage)
}
// stringArrayFlag is a simple implementation of the flag.Value interface to provide array of flags.
type stringArrayFlag struct {
dest *[]string
defaults bool // set to true if dest points to the default value.
}
func (v *stringArrayFlag) String() string {
return fmt.Sprintf("%v", *v.dest)
}
func (v *stringArrayFlag) Set(value string) error {
// Don't modify/append to the default array, create a new one.
if v.defaults {
*v.dest = []string{}
v.defaults = false
}
*v.dest = append(*v.dest, value)
return nil
}
func (fs *GoFlagSet) StringArrayVar(p *[]string, name string, value []string, usage string) {
if len(value) > 0 {
*p = value[:]
}
fs.Var(&stringArrayFlag{dest: p, defaults: true}, name, usage)
}
// GoFlag wraps a flag.Flag object from the go standard library
// and implements the Flag interface above.
type GoFlag struct {
*flag.Flag
}
func (gf *GoFlag) Name() string {
return gf.Flag.Name
}
func (gf *GoFlag) Set(value string) error {
err := gf.Flag.Value.Set(value)
if err != nil {
return err
}
gf.Flag.DefValue = value
return nil
}
func (gf *GoFlag) SetContent(name string, data []byte) error {
result, err := SetContent(gf.Flag.Value, name, data)
if err != nil {
return err
}
gf.Flag.DefValue = result
return nil
}
// Command represents a command line command.
type Command interface {
Name() string
Hide(bool)
}
type CommandDefinition struct {
Name string
Use string
Short string
Long string
Example string
Aliases []string
}
type FlagDefinition struct {
Name string
Help string
Default string
}
type FlagArg struct {
*FlagDefinition
flag.Value
}
type CommandAction func(flags []FlagArg, args []string) error
// Commander is a Command that is capable of having subcommands.
type Commander interface {
Command
AddCommand(def CommandDefinition, fl []FlagDefinition, action CommandAction) error
}
// An Augmenter is an object capable of providing default flag values, disable, add
// or modify sub commands of a generic CLI.
//
// Typically, it is invoked by a library that iterates over the flags of a command,
// and the existing commands defined.
//
// VisitFlag is invoked for each flag, with the method implementation allowed to call
// arbitrary methods on the flag.
//
// VisitCommands is invoked for each sub-command, with the method implementation allowed
// to call arbitrary methods on the command.
//
// At the end of the walk, Done is called.
//
// The user of Augmenter must assume that any of the pointers passed to the agumenter
// may be used until the point that Done() is invoked.
//
// Some resolvers may, for example, accumulate all the required flags to determine
// the value to lookup in a database with a single query.
//
// Concurrent access to the flag or command by the resolver is not allowed. The
// resolver must ensure that access to a given flag object is serialized.
type Augmenter interface {
// VisitCommand is used to ask the Augmenter to configure a command.
//
// namespace is a string that identifies the parent command this command is defined on.
// It is generally a string like "enkit.astore" identifying the "astore" subcommand of "enkit".
//
// Note that the caller will immediately call VisitFlag and other VisitCommand after this
// command returns, without waiting for Done().
VisitCommand(namespace string, command Command) (bool, error)
// VisitFlag is used to ask the Augmenter to configure a flag.
//
// namespace is a string that identifies the command the flag is defined on.
// It is generally a string like "enkit.astore" identifying the "astore" subcommand of "enkit".
VisitFlag(namespace string, flag Flag) (bool, error)
// Waits for all the visit details to be filled in.
//
// After Done() is invoked, the caller can assume that the flags and commands will no longer
// be touched by the augmenter.
Done() error
}
// SetContent is a utility function Augmenters can use to set the value of a flag.
//
// Let's say you have a flag that takes the path of a file, to load it. At run
// time, the value of the flag is the content of the file, rather than its path.
//
// SetContent will check to see if the flag implements the ContentValue interface,
// and set the content as necessary.
//
// The first string returned provides a suitable default value to show the user.
func SetContent(fl flag.Value, name string, value []byte) (string, error) {
content, ok := fl.(ContentValue)
if ok {
return "<content>", content.SetContent(name, value)
}
newv := strings.TrimSpace(string(value))
return newv, fl.Set(newv)
}
// Populator is a function that given a set of Augmenters, it is capable of invoking
// them to assign default flag values.
type Populator func(resolvers ...Augmenter) error
// GoPopulator returns a Populator capable of walking all the flags in the specified
// set, and assign defaults to those flags.
func GoPopulator(set *flag.FlagSet) Populator {
return func(resolvers ...Augmenter) error {
return PopulateDefaults(set, resolvers...)
}
}
// PopulateDefaults will provide the defaults of your flags.
//
// It should be called before `flag.Parse()` to provide defaults to your flags.
//
// If you don't use FlagSet explicitly, you can just pass flag.CommandLine.
//
// For example, an application reading flags from the environment may use:
//
// var server = flag.String("server", "127.0.0.1", "server to connect to")
// func main(...) {
// kflags.PopulateDefaults(flag.CommandLine, kflags.NewEnvAugmenter())
// [...]
//
// flag.Parse()
func PopulateDefaults(set *flag.FlagSet, resolvers ...Augmenter) error {
errors := []error{}
namespace := filepath.Base(set.Name())
user_set := map[string]struct{}{}
set.Visit(func(fl *flag.Flag) {
user_set[fl.Name] = struct{}{}
})
// One resolver can provide defaults for flags used by the next resolver.
for _, r := range resolvers {
set.VisitAll(func(fl *flag.Flag) {
// Never override flags explicitly set by users.
if _, found := user_set[fl.Name]; found {
return
}
if _, err := r.VisitFlag(namespace, &GoFlag{fl}); err != nil {
errors = append(errors, err)
}
})
if err := r.Done(); r != nil {
errors = append(errors, err)
}
}
return multierror.New(errors)
}