-
-
Notifications
You must be signed in to change notification settings - Fork 10
/
user_options.go
542 lines (499 loc) · 19.7 KB
/
user_options.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
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
// This file is part of go-getoptions.
//
// Copyright (C) 2015-2024 David Gamba Rios
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package getoptions
import (
"os"
"strings"
"github.com/DavidGamba/go-getoptions/internal/option"
)
// ModifyFn - Function signature for functions that modify an option.
type ModifyFn func(parent *GetOpt, option *option.Option)
// ModifyFn has to include the parent information because We want alias to be a
// global option. That is, that the user can call the top level opt.Alias from
// an option that belongs to a command or a subcommand. The problem with that
// is that if the ModifyFn signature doesn't provide information about the
// current parent we loose information about where the alias belongs to.
//
// The other complication with aliases becomes validation. Ideally, due to the
// tree nature of the command/option definition, you might want to define the
// same option with the same alias for two commands and they could do different
// things. That means that, without parent information, to write validation for
// aliases one has to navigate all leafs of the tree and validate that
// duplicates don't exist and limit functionality.
// Alias - Adds aliases to an option.
func (gopt *GetOpt) Alias(alias ...string) ModifyFn {
// We want alias to be a global option. That is, that the user can call
// the top level opt.Alias from an option that belongs to a command or a subcommand.
return func(parent *GetOpt, opt *option.Option) {
opt.SetAlias(alias...)
for _, a := range alias {
parent.programTree.AddChildOption(a, opt)
}
}
}
// Description - Add a description to an option for use in automated help.
func (gopt *GetOpt) Description(msg string) ModifyFn {
return func(parent *GetOpt, opt *option.Option) {
opt.Description = msg
}
}
// SetCalled - Mark the option as called using the option name.
// Useful when adding options to a CommandFn call from a wrapper function.
func (gopt *GetOpt) SetCalled(called bool) ModifyFn {
return func(parent *GetOpt, opt *option.Option) {
opt.Called = called
}
}
// Required - Automatically return an error if the option is not called.
// Optionally provide a custom error message, a default error message will be used otherwise.
func (gopt *GetOpt) Required(msg ...string) ModifyFn {
var errTxt string
if len(msg) >= 1 {
errTxt = msg[0]
}
return func(parent *GetOpt, opt *option.Option) {
opt.SetRequired(errTxt)
}
}
// GetEnv - Will read an environment variable if set.
// Precedence higher to lower: CLI option, environment variable, option default.
//
// Currently, only `opt.Bool`, `opt.BoolVar`, `opt.String`, and `opt.StringVar` are supported.
//
// When an environment variable that matches the variable from opt.GetEnv is
// set, opt.GetEnv will set opt.Called(name) to true and will set
// opt.CalledAs(name) to the name of the environment variable used.
// In other words, when an option is required (opt.Required is set) opt.GetEnv
// satisfies that requirement.
//
// When using `opt.GetEnv` with `opt.Bool` or `opt.BoolVar`, only the words
// "true" or "false" are valid. They can be provided in any casing, for
// example: "true", "True" or "TRUE".
//
// NOTE: Non supported option types behave with a No-Op when `opt.GetEnv` is defined.
func (gopt *GetOpt) GetEnv(name string) ModifyFn {
return func(parent *GetOpt, opt *option.Option) {
opt.SetEnvVar(name)
value := os.Getenv(name)
if value != "" {
switch opt.OptType {
case option.BoolType:
v := strings.ToLower(value)
if v == "true" || v == "false" {
_ = opt.Save(v)
opt.SetCalled(name)
}
case option.StringType,
option.IntType,
option.Float64Type,
option.StringOptionalType,
option.IntOptionalType,
option.Float64OptionalType:
_ = opt.Save(value)
opt.SetCalled(name)
}
}
}
}
// ArgName - Add an argument name to an option for use in automated help.
// For example, by default a string option will have a default synopsis as follows:
//
// --host <string>
//
// If ArgName("hostname") is used, the synopsis will read:
//
// --host <hostname>
func (gopt *GetOpt) ArgName(name string) ModifyFn {
return func(parent *GetOpt, opt *option.Option) {
opt.SetHelpArgName(name)
}
}
// ValidValues - adds a list of enforced valid values for the option.
// These are also added to the autocompletion engine.
func (gopt *GetOpt) ValidValues(values ...string) ModifyFn {
return func(parent *GetOpt, opt *option.Option) {
opt.ValidValues = append(opt.ValidValues, values...)
opt.SuggestedValues = opt.ValidValues
}
}
// SuggestedValues - adds a list of suggestions to the autocompletion for the option.
func (gopt *GetOpt) SuggestedValues(values ...string) ModifyFn {
return func(parent *GetOpt, opt *option.Option) {
opt.SuggestedValues = append(opt.SuggestedValues, values...)
}
}
// Called - Indicates if the option was passed on the command line.
// If the `name` is an option that wasn't declared it will return false.
func (gopt *GetOpt) Called(name string) bool {
if name == "" {
// Don't panic at this point since the user can only reproduce this by
// executing every branch of their code.
return false
}
if v, ok := gopt.programTree.ChildOptions[name]; ok {
return v.Called
}
return false
}
// CalledAs - Returns the alias used to call the option.
// Empty string otherwise.
//
// If the `name` is an option that wasn't declared it will return an empty string.
//
// For options that can be called multiple times, the last alias used is returned.
func (gopt *GetOpt) CalledAs(name string) string {
if name == "" {
// Don't panic at this point since the user can only reproduce this by
// executing every branch of their code.
return ""
}
if v, ok := gopt.programTree.ChildOptions[name]; ok {
return v.UsedAlias
}
return ""
}
// Value - Returns the value of the given option.
//
// Type assertions are required in cases where the compiler can't determine the type by context.
// For example: `opt.Value("flag").(bool)`.
func (gopt *GetOpt) Value(name string) interface{} {
if v, ok := gopt.programTree.ChildOptions[name]; ok {
return v.Value()
}
return nil
}
// SetValue - Set the value of the given option using strings as an argument.
//
// Examples:
//
// opt.SetValue("bool") // boolean - sets to opposite of default
// opt.SetValue("int", "123") // int
// err := opt.SetValue("float64", "x") // error because "x" is not a valid float64
// opt.SetValue("slice", "a", "b", "c") // []string
// opt.SetValue("slice", "d", "e", "f") // Can be called multiple times for options that allow it
// opt.SetValue("map", "hello=world", "hola=mundo") // map[string]string
func (gopt *GetOpt) SetValue(name string, value ...string) error {
if v, ok := gopt.programTree.ChildOptions[name]; ok {
return v.Save(value...)
}
return ErrorNotFound
}
// Bool - define a `bool` option and its aliases.
// It returns a `*bool` pointing to the variable holding the result.
// If the option is found, the result will be the opposite of the provided default.
func (gopt *GetOpt) Bool(name string, def bool, fns ...ModifyFn) *bool {
gopt.BoolVar(&def, name, def, fns...)
return &def
}
// BoolVar - define a `bool` option and its aliases.
// The result will be available through the variable marked by the given pointer.
// If the option is found, the result will be the opposite of the provided default.
func (gopt *GetOpt) BoolVar(p *bool, name string, def bool, fns ...ModifyFn) {
*p = def
n := option.New(name, option.BoolType, p)
gopt.programTree.AddChildOption(name, n)
for _, fn := range fns {
fn(gopt, n)
}
}
// String - define a `string` option and its aliases.
// If not called, the return value will be that of the given default `def`.
func (gopt *GetOpt) String(name, def string, fns ...ModifyFn) *string {
gopt.StringVar(&def, name, def, fns...)
return &def
}
// StringVar - define a `string` option and its aliases.
// The result will be available through the variable marked by the given pointer.
// If not called, the return value will be that of the given default `def`.
func (gopt *GetOpt) StringVar(p *string, name, def string, fns ...ModifyFn) {
*p = def
n := option.New(name, option.StringType, p)
gopt.programTree.AddChildOption(name, n)
for _, fn := range fns {
fn(gopt, n)
}
}
// StringOptional - define a `string` option and its aliases.
//
// StringOptional will set the string to the provided default value when no value is given.
// For example, when called with `--strOpt value`, the value is `value`.
// when called with `--strOpt` the value is the given default.
func (gopt *GetOpt) StringOptional(name, def string, fns ...ModifyFn) *string {
gopt.StringVarOptional(&def, name, def, fns...)
return &def
}
// StringVarOptional - define a `string` option and its aliases.
// The result will be available through the variable marked by the given pointer.
//
// StringVarOptional will set the string to the provided default value when no value is given.
// For example, when called with `--strOpt value`, the value is `value`.
// when called with `--strOpt` the value is the given default.
func (gopt *GetOpt) StringVarOptional(p *string, name, def string, fns ...ModifyFn) {
*p = def
n := option.New(name, option.StringOptionalType, p)
gopt.programTree.AddChildOption(name, n)
for _, fn := range fns {
fn(gopt, n)
}
}
// StringSlice - define a `[]string` option and its aliases.
//
// StringSlice will accept multiple calls to the same option and append them
// to the `[]string`.
// For example, when called with `--strRpt 1 --strRpt 2`, the value is `[]string{"1", "2"}`.
//
// Additionally, StringSlice will allow to define a min and max amount of
// arguments to be passed at once.
// For example, when min is 1 and max is 3 and called with `--strRpt 1 2 3`,
// the value is `[]string{"1", "2", "3"}`.
// It could also be called with `--strRpt 1 --strRpt 2 --strRpt 3` for the same result.
//
// When min is bigger than 1, it is required to pass the amount of arguments defined by min at once.
// For example: with `min = 2`, you at least require `--strRpt 1 2 --strRpt 3`
func (gopt *GetOpt) StringSlice(name string, min, max int, fns ...ModifyFn) *[]string {
s := []string{}
gopt.StringSliceVar(&s, name, min, max, fns...)
return &s
}
// StringSliceVar - define a `[]string` option and its aliases.
//
// StringSliceVar will accept multiple calls to the same option and append them
// to the `[]string`.
// For example, when called with `--strRpt 1 --strRpt 2`, the value is `[]string{"1", "2"}`.
//
// Additionally, StringSliceVar will allow to define a min and max amount of
// arguments to be passed at once.
// For example, when min is 1 and max is 3 and called with `--strRpt 1 2 3`,
// the value is `[]string{"1", "2", "3"}`.
// It could also be called with `--strRpt 1 --strRpt 2 --strRpt 3` for the same result.
//
// When min is bigger than 1, it is required to pass the amount of arguments defined by min at once.
// For example: with `min = 2`, you at least require `--strRpt 1 2 --strRpt 3`
func (gopt *GetOpt) StringSliceVar(p *[]string, name string, min, max int, fns ...ModifyFn) {
n := option.New(name, option.StringRepeatType, p)
n.MinArgs = min
n.MaxArgs = max
gopt.programTree.AddChildOption(name, n)
for _, fn := range fns {
fn(gopt, n)
}
n.Synopsis()
}
// Int - define an `int` option and its aliases.
func (gopt *GetOpt) Int(name string, def int, fns ...ModifyFn) *int {
gopt.IntVar(&def, name, def, fns...)
return &def
}
// IntVar - define an `int` option and its aliases.
// The result will be available through the variable marked by the given pointer.
func (gopt *GetOpt) IntVar(p *int, name string, def int, fns ...ModifyFn) {
*p = def
n := option.New(name, option.IntType, p)
gopt.programTree.AddChildOption(name, n)
for _, fn := range fns {
fn(gopt, n)
}
}
// IntOptional - define a `int` option and its aliases.
//
// IntOptional will set the int to the provided default value when no value is given.
// For example, when called with `--intOpt 123`, the value is `123`.
// when called with `--intOpt` the value is the given default.
func (gopt *GetOpt) IntOptional(name string, def int, fns ...ModifyFn) *int {
gopt.IntVarOptional(&def, name, def, fns...)
return &def
}
// IntVarOptional - define a `int` option and its aliases.
// The result will be available through the variable marked by the given pointer.
//
// IntOptional will set the int to the provided default value when no value is given.
// For example, when called with `--intOpt 123`, the value is `123`.
// when called with `--intOpt` the value is the given default.
func (gopt *GetOpt) IntVarOptional(p *int, name string, def int, fns ...ModifyFn) {
*p = def
n := option.New(name, option.IntOptionalType, p)
gopt.programTree.AddChildOption(name, n)
for _, fn := range fns {
fn(gopt, n)
}
}
// IntSlice - define a `[]int` option and its aliases.
//
// IntSlice will accept multiple calls to the same option and append them
// to the `[]int`.
// For example, when called with `--intRpt 1 --intRpt 2`, the value is `[]int{1, 2}`.
//
// Additionally, IntSlice will allow to define a min and max amount of
// arguments to be passed at once.
// For example, when min is 1 and max is 3 and called with `--strRpt 1 2 3`,
// the value is `[]int{1, 2, 3}`.
// It could also be called with `--strRpt 1 --strRpt 2 --strRpt 3` for the same result.
//
// When min is bigger than 1, it is required to pass the amount of arguments defined by min at once.
// For example: with `min = 2`, you at least require `--strRpt 1 2 --strRpt 3`
//
// Finally, positive integer ranges are allowed.
// For example, Instead of writing: `csv --columns 1 2 3` or
// `csv --columns 1 --columns 2 --columns 3`
// The input could be: `csv --columns 1..3`.
func (gopt *GetOpt) IntSlice(name string, min, max int, fns ...ModifyFn) *[]int {
s := []int{}
gopt.IntSliceVar(&s, name, min, max, fns...)
return &s
}
// IntSliceVar - define a `[]int` option and its aliases.
//
// IntSliceVar will accept multiple calls to the same option and append them
// to the `[]int`.
// For example, when called with `--intRpt 1 --intRpt 2`, the value is `[]int{1, 2}`.
//
// Additionally, IntSliceVar will allow to define a min and max amount of
// arguments to be passed at once.
// For example, when min is 1 and max is 3 and called with `--strRpt 1 2 3`,
// the value is `[]int{1, 2, 3}`.
// It could also be called with `--strRpt 1 --strRpt 2 --strRpt 3` for the same result.
//
// When min is bigger than 1, it is required to pass the amount of arguments defined by min at once.
// For example: with `min = 2`, you at least require `--strRpt 1 2 --strRpt 3`
//
// Finally, positive integer ranges are allowed.
// For example, Instead of writing: `csv --columns 1 2 3` or
// `csv --columns 1 --columns 2 --columns 3`
// The input could be: `csv --columns 1..3`.
func (gopt *GetOpt) IntSliceVar(p *[]int, name string, min, max int, fns ...ModifyFn) {
n := option.New(name, option.IntRepeatType, p)
n.MinArgs = min
n.MaxArgs = max
gopt.programTree.AddChildOption(name, n)
for _, fn := range fns {
fn(gopt, n)
}
n.Synopsis()
}
// Increment - When called multiple times it increments the int counter defined by this option.
func (gopt *GetOpt) Increment(name string, def int, fns ...ModifyFn) *int {
gopt.IncrementVar(&def, name, def, fns...)
return &def
}
// IncrementVar - When called multiple times it increments the provided int.
func (gopt *GetOpt) IncrementVar(p *int, name string, def int, fns ...ModifyFn) {
*p = def
n := option.New(name, option.IncrementType, p)
gopt.programTree.AddChildOption(name, n)
for _, fn := range fns {
fn(gopt, n)
}
}
// Float64 - define an `float64` option and its aliases.
func (gopt *GetOpt) Float64(name string, def float64, fns ...ModifyFn) *float64 {
gopt.Float64Var(&def, name, def, fns...)
return &def
}
// Float64Var - define an `float64` option and its aliases.
// The result will be available through the variable marked by the given pointer.
func (gopt *GetOpt) Float64Var(p *float64, name string, def float64, fns ...ModifyFn) {
*p = def
n := option.New(name, option.Float64Type, p)
gopt.programTree.AddChildOption(name, n)
for _, fn := range fns {
fn(gopt, n)
}
}
// Float64Optional - define an `float64` option and its aliases.
func (gopt *GetOpt) Float64Optional(name string, def float64, fns ...ModifyFn) *float64 {
gopt.Float64VarOptional(&def, name, def, fns...)
return &def
}
// Float64VarOptional - define an `float64` option and its aliases.
// The result will be available through the variable marked by the given pointer.
func (gopt *GetOpt) Float64VarOptional(p *float64, name string, def float64, fns ...ModifyFn) {
*p = def
n := option.New(name, option.Float64OptionalType, p)
gopt.programTree.AddChildOption(name, n)
for _, fn := range fns {
fn(gopt, n)
}
}
func (gopt *GetOpt) Float64Slice(name string, min, max int, fns ...ModifyFn) *[]float64 {
s := []float64{}
gopt.Float64SliceVar(&s, name, min, max, fns...)
return &s
}
func (gopt *GetOpt) Float64SliceVar(p *[]float64, name string, min, max int, fns ...ModifyFn) {
n := option.New(name, option.Float64RepeatType, p)
n.MinArgs = min
n.MaxArgs = max
gopt.programTree.AddChildOption(name, n)
for _, fn := range fns {
fn(gopt, n)
}
n.Synopsis()
}
// StringMap - define a `map[string]string` option and its aliases.
//
// StringMap will accept multiple calls of `key=value` type to the same option
// and add them to the `map[string]string` result.
// For example, when called with `--strMap k=v --strMap k2=v2`, the value is
// `map[string]string{"k":"v", "k2": "v2"}`.
//
// Additionally, StringMap will allow to define a min and max amount of
// arguments to be passed at once.
// For example, when min is 1 and max is 3 and called with `--strMap k=v k2=v2 k3=v3`,
// the value is `map[string]string{"k":"v", "k2": "v2", "k3": "v3"}`.
// It could also be called with `--strMap k=v --strMap k2=v2 --strMap k3=v3` for the same result.
//
// When min is bigger than 1, it is required to pass the amount of arguments defined by min at once.
// For example: with `min = 2`, you at least require `--strMap k=v k2=v2 --strMap k3=v3`
func (gopt *GetOpt) StringMap(name string, min, max int, fns ...ModifyFn) map[string]string {
m := map[string]string{}
gopt.StringMapVar(&m, name, min, max, fns...)
return m
}
// StringMapVar - define a `map[string]string` option and its aliases.
//
// StringMapVar will accept multiple calls of `key=value` type to the same option
// and add them to the `map[string]string` result.
// For example, when called with `--strMap k=v --strMap k2=v2`, the value is
// `map[string]string{"k":"v", "k2": "v2"}`.
//
// Additionally, StringMapVar will allow to define a min and max amount of
// arguments to be passed at once.
// For example, when min is 1 and max is 3 and called with `--strMap k=v k2=v2 k3=v3`,
// the value is `map[string]string{"k":"v", "k2": "v2", "k3": "v3"}`.
// It could also be called with `--strMap k=v --strMap k2=v2 --strMap k3=v3` for the same result.
//
// When min is bigger than 1, it is required to pass the amount of arguments defined by min at once.
// For example: with `min = 2`, you at least require `--strMap k=v k2=v2 --strMap k3=v3`
func (gopt *GetOpt) StringMapVar(m *map[string]string, name string, min, max int, fns ...ModifyFn) {
// check that the map has been initialized
if *m == nil {
*m = make(map[string]string)
}
n := option.New(name, option.StringMapType, m)
n.MinArgs = min
n.MaxArgs = max
gopt.programTree.AddChildOption(name, n)
for _, fn := range fns {
fn(gopt, n)
}
n.Synopsis()
}
// SetMapKeysToLower - StringMap keys captured from StringMap are lower case.
// For example:
//
// command --opt key=value
//
// And:
//
// command --opt KEY=value
//
// Would both return `map[string]string{"key":"value"}`.
func (gopt *GetOpt) SetMapKeysToLower() *GetOpt {
gopt.programTree.mapKeysToLower = true
return gopt
}