forked from elastic/beats
-
Notifications
You must be signed in to change notification settings - Fork 1
/
flags.go
273 lines (232 loc) · 6.9 KB
/
flags.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
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
package common
import (
"flag"
"strings"
ucfg "github.com/elastic/go-ucfg"
cfgflag "github.com/elastic/go-ucfg/flag"
)
// StringsFlag collects multiple usages of the same flag into an array of strings.
// Duplicate values will be ignored.
type StringsFlag struct {
list *[]string
isDefault bool
flag *flag.Flag
}
// SettingsFlag captures key/values pairs into an Config object.
// The flag backed by SettingsFlag can be used multiple times.
// Values are overwritten by the last usage of a key.
type SettingsFlag cfgflag.FlagValue
// flagOverwrite provides a flag value, which always overwrites the same setting
// in an Config object.
type flagOverwrite struct {
config *ucfg.Config
path string
value string
}
// StringArrFlag creates and registers a new StringsFlag with the given FlagSet.
// If no FlagSet is passed, flag.CommandLine will be used as target FlagSet.
func StringArrFlag(fs *flag.FlagSet, name, def, usage string) *StringsFlag {
var arr *[]string
if def != "" {
arr = &[]string{def}
} else {
arr = &[]string{}
}
return StringArrVarFlag(fs, arr, name, usage)
}
// StringArrVarFlag creates and registers a new StringsFlag with the given
// FlagSet. Results of the flag usage will be appended to `arr`. If the slice
// is not initially empty, its first value will be used as default. If the flag
// is used, the slice will be emptied first. If no FlagSet is passed,
// flag.CommandLine will be used as target FlagSet.
func StringArrVarFlag(fs *flag.FlagSet, arr *[]string, name, usage string) *StringsFlag {
if fs == nil {
fs = flag.CommandLine
}
f := NewStringsFlag(arr)
f.Register(fs, name, usage)
return f
}
// NewStringsFlag creates a new, but unregistered StringsFlag instance.
// Results of the flag usage will be appended to `arr`. If the slice is not
// initially empty, its first value will be used as default. If the flag is
// used, the slice will be emptied first.
func NewStringsFlag(arr *[]string) *StringsFlag {
if arr == nil {
panic("No target array")
}
return &StringsFlag{list: arr, isDefault: true}
}
// Register registers the StringsFlag instance with a FlagSet.
// A valid FlagSet must be used.
// Register panics if the flag is already registered.
func (f *StringsFlag) Register(fs *flag.FlagSet, name, usage string) {
if f.flag != nil {
panic("StringsFlag is already registered")
}
fs.Var(f, name, usage)
f.flag = fs.Lookup(name)
if f.flag == nil {
panic("Failed to lookup registered flag")
}
if len(*f.list) > 0 {
f.flag.DefValue = (*f.list)[0]
}
}
// String joins all it's values set into a comma-separated string.
func (f *StringsFlag) String() string {
if f == nil || f.list == nil {
return ""
}
l := *f.list
return strings.Join(l, ", ")
}
// SetDefault sets the flags new default value.
// This overwrites the contents in the backing array.
func (f *StringsFlag) SetDefault(v string) {
if f.flag != nil {
f.flag.DefValue = v
}
*f.list = []string{v}
f.isDefault = true
}
// Set is used to pass usage of the flag to StringsFlag. Set adds the new value
// to the backing array. The array will be emptied on Set, if the backing array
// still contains the default value.
func (f *StringsFlag) Set(v string) error {
// Ignore duplicates, can be caused by multiple flag parses
if f.isDefault {
*f.list = []string{v}
} else {
for _, old := range *f.list {
if old == v {
return nil
}
}
*f.list = append(*f.list, v)
}
f.isDefault = false
return nil
}
// Get returns the backing slice its contents as interface{}. The type used is
// `[]string`.
func (f *StringsFlag) Get() interface{} {
return f.List()
}
// List returns the current set values.
func (f *StringsFlag) List() []string {
return *f.list
}
// Type reports the type of contents (string) expected to be parsed by Set.
// It is used to build the CLI usage string.
func (f *StringsFlag) Type() string {
return "string"
}
// SettingFlag defines a setting flag, name and it's usage. The return value is
// the Config object settings are applied to.
func SettingFlag(fs *flag.FlagSet, name, usage string) *Config {
cfg := NewConfig()
SettingVarFlag(fs, cfg, name, usage)
return cfg
}
// SettingVarFlag defines a setting flag, name and it's usage.
// Settings are applied to the Config object passed.
func SettingVarFlag(fs *flag.FlagSet, def *Config, name, usage string) {
if fs == nil {
fs = flag.CommandLine
}
f := NewSettingsFlag(def)
fs.Var(f, name, usage)
}
// NewSettingsFlag creates a new SettingsFlag instance, not registered with any
// FlagSet.
func NewSettingsFlag(def *Config) *SettingsFlag {
opts := append(
[]ucfg.Option{
ucfg.MetaData(ucfg.Meta{Source: "command line flag"}),
},
configOpts...,
)
tmp := cfgflag.NewFlagKeyValue(def.access(), true, opts...)
return (*SettingsFlag)(tmp)
}
func (f *SettingsFlag) access() *cfgflag.FlagValue {
return (*cfgflag.FlagValue)(f)
}
// Config returns the config object the SettingsFlag stores applied settings to.
func (f *SettingsFlag) Config() *Config {
return fromConfig(f.access().Config())
}
// Set sets a settings value in the Config object. The input string must be a
// key-value pair like `key=value`. If the value is missing, the value is set
// to the boolean value `true`.
func (f *SettingsFlag) Set(s string) error {
return f.access().Set(s)
}
// Get returns the Config object used to store values.
func (f *SettingsFlag) Get() interface{} {
return f.Config()
}
// String always returns an empty string. It is required to fulfil
// the flag.Value interface.
func (f *SettingsFlag) String() string {
return ""
}
// Type reports the type of contents (setting=value) expected to be parsed by Set.
// It is used to build the CLI usage string.
func (f *SettingsFlag) Type() string {
return "setting=value"
}
// ConfigOverwriteFlag defines a new flag updating a setting in an Config
// object. The name is used as the flag its name the path parameter is the
// full setting name to be used when the flag is set.
func ConfigOverwriteFlag(
fs *flag.FlagSet,
config *Config,
name, path, def, usage string,
) *string {
if config == nil {
panic("Missing configuration")
}
if path == "" {
panic("empty path")
}
if fs == nil {
fs = flag.CommandLine
}
if def != "" {
err := config.SetString(path, -1, def)
if err != nil {
panic(err)
}
}
f := newOverwriteFlag(config, path, def)
fs.Var(f, name, usage)
return &f.value
}
func newOverwriteFlag(config *Config, path, def string) *flagOverwrite {
return &flagOverwrite{config: config.access(), path: path, value: def}
}
func (f *flagOverwrite) String() string {
return f.value
}
func (f *flagOverwrite) Set(v string) error {
opts := append(
[]ucfg.Option{
ucfg.MetaData(ucfg.Meta{Source: "command line flag"}),
},
configOpts...,
)
err := f.config.SetString(f.path, -1, v, opts...)
if err != nil {
return err
}
f.value = v
return nil
}
func (f *flagOverwrite) Get() interface{} {
return f.value
}
func (f *flagOverwrite) Type() string {
return "string"
}