1package flags
2
3import (
4	"bytes"
5	"fmt"
6	"os"
7	"reflect"
8	"strings"
9	"unicode/utf8"
10)
11
12// Option flag information. Contains a description of the option, short and
13// long name as well as a default value and whether an argument for this
14// flag is optional.
15type Option struct {
16	// The description of the option flag. This description is shown
17	// automatically in the built-in help.
18	Description string
19
20	// The short name of the option (a single character). If not 0, the
21	// option flag can be 'activated' using -<ShortName>. Either ShortName
22	// or LongName needs to be non-empty.
23	ShortName rune
24
25	// The long name of the option. If not "", the option flag can be
26	// activated using --<LongName>. Either ShortName or LongName needs
27	// to be non-empty.
28	LongName string
29
30	// The default value of the option.
31	Default []string
32
33	// The optional environment default value key name.
34	EnvDefaultKey string
35
36	// The optional delimiter string for EnvDefaultKey values.
37	EnvDefaultDelim string
38
39	// If true, specifies that the argument to an option flag is optional.
40	// When no argument to the flag is specified on the command line, the
41	// value of OptionalValue will be set in the field this option represents.
42	// This is only valid for non-boolean options.
43	OptionalArgument bool
44
45	// The optional value of the option. The optional value is used when
46	// the option flag is marked as having an OptionalArgument. This means
47	// that when the flag is specified, but no option argument is given,
48	// the value of the field this option represents will be set to
49	// OptionalValue. This is only valid for non-boolean options.
50	OptionalValue []string
51
52	// If true, the option _must_ be specified on the command line. If the
53	// option is not specified, the parser will generate an ErrRequired type
54	// error.
55	Required bool
56
57	// A name for the value of an option shown in the Help as --flag [ValueName]
58	ValueName string
59
60	// A mask value to show in the help instead of the default value. This
61	// is useful for hiding sensitive information in the help, such as
62	// passwords.
63	DefaultMask string
64
65	// If non empty, only a certain set of values is allowed for an option.
66	Choices []string
67
68	// If true, the option is not displayed in the help or man page
69	Hidden bool
70
71	// The group which the option belongs to
72	group *Group
73
74	// The struct field which the option represents.
75	field reflect.StructField
76
77	// The struct field value which the option represents.
78	value reflect.Value
79
80	// Determines if the option will be always quoted in the INI output
81	iniQuote bool
82
83	tag                     multiTag
84	isSet                   bool
85	isSetDefault            bool
86	preventDefault          bool
87	clearReferenceBeforeSet bool
88
89	defaultLiteral string
90}
91
92// LongNameWithNamespace returns the option's long name with the group namespaces
93// prepended by walking up the option's group tree. Namespaces and the long name
94// itself are separated by the parser's namespace delimiter. If the long name is
95// empty an empty string is returned.
96func (option *Option) LongNameWithNamespace() string {
97	if len(option.LongName) == 0 {
98		return ""
99	}
100
101	// fetch the namespace delimiter from the parser which is always at the
102	// end of the group hierarchy
103	namespaceDelimiter := ""
104	g := option.group
105
106	for {
107		if p, ok := g.parent.(*Parser); ok {
108			namespaceDelimiter = p.NamespaceDelimiter
109
110			break
111		}
112
113		switch i := g.parent.(type) {
114		case *Command:
115			g = i.Group
116		case *Group:
117			g = i
118		}
119	}
120
121	// concatenate long name with namespace
122	longName := option.LongName
123	g = option.group
124
125	for g != nil {
126		if g.Namespace != "" {
127			longName = g.Namespace + namespaceDelimiter + longName
128		}
129
130		switch i := g.parent.(type) {
131		case *Command:
132			g = i.Group
133		case *Group:
134			g = i
135		case *Parser:
136			g = nil
137		}
138	}
139
140	return longName
141}
142
143// EnvKeyWithNamespace returns the option's env key with the group namespaces
144// prepended by walking up the option's group tree. Namespaces and the env key
145// itself are separated by the parser's namespace delimiter. If the env key is
146// empty an empty string is returned.
147func (option *Option) EnvKeyWithNamespace() string {
148	if len(option.EnvDefaultKey) == 0 {
149		return ""
150	}
151
152	// fetch the namespace delimiter from the parser which is always at the
153	// end of the group hierarchy
154	namespaceDelimiter := ""
155	g := option.group
156
157	for {
158		if p, ok := g.parent.(*Parser); ok {
159			namespaceDelimiter = p.EnvNamespaceDelimiter
160
161			break
162		}
163
164		switch i := g.parent.(type) {
165		case *Command:
166			g = i.Group
167		case *Group:
168			g = i
169		}
170	}
171
172	// concatenate long name with namespace
173	key := option.EnvDefaultKey
174	g = option.group
175
176	for g != nil {
177		if g.EnvNamespace != "" {
178			key = g.EnvNamespace + namespaceDelimiter + key
179		}
180
181		switch i := g.parent.(type) {
182		case *Command:
183			g = i.Group
184		case *Group:
185			g = i
186		case *Parser:
187			g = nil
188		}
189	}
190
191	return key
192}
193
194// String converts an option to a human friendly readable string describing the
195// option.
196func (option *Option) String() string {
197	var s string
198	var short string
199
200	if option.ShortName != 0 {
201		data := make([]byte, utf8.RuneLen(option.ShortName))
202		utf8.EncodeRune(data, option.ShortName)
203		short = string(data)
204
205		if len(option.LongName) != 0 {
206			s = fmt.Sprintf("%s%s, %s%s",
207				string(defaultShortOptDelimiter), short,
208				defaultLongOptDelimiter, option.LongNameWithNamespace())
209		} else {
210			s = fmt.Sprintf("%s%s", string(defaultShortOptDelimiter), short)
211		}
212	} else if len(option.LongName) != 0 {
213		s = fmt.Sprintf("%s%s", defaultLongOptDelimiter, option.LongNameWithNamespace())
214	}
215
216	return s
217}
218
219// Value returns the option value as an interface{}.
220func (option *Option) Value() interface{} {
221	return option.value.Interface()
222}
223
224// Field returns the reflect struct field of the option.
225func (option *Option) Field() reflect.StructField {
226	return option.field
227}
228
229// IsSet returns true if option has been set
230func (option *Option) IsSet() bool {
231	return option.isSet
232}
233
234// IsSetDefault returns true if option has been set via the default option tag
235func (option *Option) IsSetDefault() bool {
236	return option.isSetDefault
237}
238
239// Set the value of an option to the specified value. An error will be returned
240// if the specified value could not be converted to the corresponding option
241// value type.
242func (option *Option) set(value *string) error {
243	kind := option.value.Type().Kind()
244
245	if (kind == reflect.Map || kind == reflect.Slice) && option.clearReferenceBeforeSet {
246		option.empty()
247	}
248
249	option.isSet = true
250	option.preventDefault = true
251	option.clearReferenceBeforeSet = false
252
253	if len(option.Choices) != 0 {
254		found := false
255
256		for _, choice := range option.Choices {
257			if choice == *value {
258				found = true
259				break
260			}
261		}
262
263		if !found {
264			allowed := strings.Join(option.Choices[0:len(option.Choices)-1], ", ")
265
266			if len(option.Choices) > 1 {
267				allowed += " or " + option.Choices[len(option.Choices)-1]
268			}
269
270			return newErrorf(ErrInvalidChoice,
271				"Invalid value `%s' for option `%s'. Allowed values are: %s",
272				*value, option, allowed)
273		}
274	}
275
276	if option.isFunc() {
277		return option.call(value)
278	} else if value != nil {
279		return convert(*value, option.value, option.tag)
280	}
281
282	return convert("", option.value, option.tag)
283}
284
285func (option *Option) setDefault(value *string) error {
286	if option.preventDefault {
287		return nil
288	}
289
290	if err := option.set(value); err != nil {
291		return err
292	}
293
294	option.isSetDefault = true
295	option.preventDefault = false
296
297	return nil
298}
299
300func (option *Option) showInHelp() bool {
301	return !option.Hidden && (option.ShortName != 0 || len(option.LongName) != 0)
302}
303
304func (option *Option) canArgument() bool {
305	if u := option.isUnmarshaler(); u != nil {
306		return true
307	}
308
309	return !option.isBool()
310}
311
312func (option *Option) emptyValue() reflect.Value {
313	tp := option.value.Type()
314
315	if tp.Kind() == reflect.Map {
316		return reflect.MakeMap(tp)
317	}
318
319	return reflect.Zero(tp)
320}
321
322func (option *Option) empty() {
323	if !option.isFunc() {
324		option.value.Set(option.emptyValue())
325	}
326}
327
328func (option *Option) clearDefault() error {
329	if option.preventDefault {
330		return nil
331	}
332
333	usedDefault := option.Default
334
335	if envKey := option.EnvKeyWithNamespace(); envKey != "" {
336		if value, ok := os.LookupEnv(envKey); ok {
337			if option.EnvDefaultDelim != "" {
338				usedDefault = strings.Split(value, option.EnvDefaultDelim)
339			} else {
340				usedDefault = []string{value}
341			}
342		}
343	}
344
345	option.isSetDefault = true
346
347	if len(usedDefault) > 0 {
348		option.empty()
349
350		for _, d := range usedDefault {
351			err := option.setDefault(&d)
352
353			if err != nil {
354				return err
355			}
356		}
357	} else {
358		tp := option.value.Type()
359
360		switch tp.Kind() {
361		case reflect.Map:
362			if option.value.IsNil() {
363				option.empty()
364			}
365		case reflect.Slice:
366			if option.value.IsNil() {
367				option.empty()
368			}
369		}
370	}
371
372	return nil
373}
374
375func (option *Option) valueIsDefault() bool {
376	// Check if the value of the option corresponds to its
377	// default value
378	emptyval := option.emptyValue()
379
380	checkvalptr := reflect.New(emptyval.Type())
381	checkval := reflect.Indirect(checkvalptr)
382
383	checkval.Set(emptyval)
384
385	if len(option.Default) != 0 {
386		for _, v := range option.Default {
387			convert(v, checkval, option.tag)
388		}
389	}
390
391	return reflect.DeepEqual(option.value.Interface(), checkval.Interface())
392}
393
394func (option *Option) isUnmarshaler() Unmarshaler {
395	v := option.value
396
397	for {
398		if !v.CanInterface() {
399			break
400		}
401
402		i := v.Interface()
403
404		if u, ok := i.(Unmarshaler); ok {
405			return u
406		}
407
408		if !v.CanAddr() {
409			break
410		}
411
412		v = v.Addr()
413	}
414
415	return nil
416}
417
418func (option *Option) isValueValidator() ValueValidator {
419	v := option.value
420
421	for {
422		if !v.CanInterface() {
423			break
424		}
425
426		i := v.Interface()
427
428		if u, ok := i.(ValueValidator); ok {
429			return u
430		}
431
432		if !v.CanAddr() {
433			break
434		}
435
436		v = v.Addr()
437	}
438
439	return nil
440}
441
442func (option *Option) isBool() bool {
443	tp := option.value.Type()
444
445	for {
446		switch tp.Kind() {
447		case reflect.Slice, reflect.Ptr:
448			tp = tp.Elem()
449		case reflect.Bool:
450			return true
451		case reflect.Func:
452			return tp.NumIn() == 0
453		default:
454			return false
455		}
456	}
457}
458
459func (option *Option) isSignedNumber() bool {
460	tp := option.value.Type()
461
462	for {
463		switch tp.Kind() {
464		case reflect.Slice, reflect.Ptr:
465			tp = tp.Elem()
466		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float32, reflect.Float64:
467			return true
468		default:
469			return false
470		}
471	}
472}
473
474func (option *Option) isFunc() bool {
475	return option.value.Type().Kind() == reflect.Func
476}
477
478func (option *Option) call(value *string) error {
479	var retval []reflect.Value
480
481	if value == nil {
482		retval = option.value.Call(nil)
483	} else {
484		tp := option.value.Type().In(0)
485
486		val := reflect.New(tp)
487		val = reflect.Indirect(val)
488
489		if err := convert(*value, val, option.tag); err != nil {
490			return err
491		}
492
493		retval = option.value.Call([]reflect.Value{val})
494	}
495
496	if len(retval) == 1 && retval[0].Type() == reflect.TypeOf((*error)(nil)).Elem() {
497		if retval[0].Interface() == nil {
498			return nil
499		}
500
501		return retval[0].Interface().(error)
502	}
503
504	return nil
505}
506
507func (option *Option) updateDefaultLiteral() {
508	defs := option.Default
509	def := ""
510
511	if len(defs) == 0 && option.canArgument() {
512		var showdef bool
513
514		switch option.field.Type.Kind() {
515		case reflect.Func, reflect.Ptr:
516			showdef = !option.value.IsNil()
517		case reflect.Slice, reflect.String, reflect.Array:
518			showdef = option.value.Len() > 0
519		case reflect.Map:
520			showdef = !option.value.IsNil() && option.value.Len() > 0
521		default:
522			zeroval := reflect.Zero(option.field.Type)
523			showdef = !reflect.DeepEqual(zeroval.Interface(), option.value.Interface())
524		}
525
526		if showdef {
527			def, _ = convertToString(option.value, option.tag)
528		}
529	} else if len(defs) != 0 {
530		l := len(defs) - 1
531
532		for i := 0; i < l; i++ {
533			def += quoteIfNeeded(defs[i]) + ", "
534		}
535
536		def += quoteIfNeeded(defs[l])
537	}
538
539	option.defaultLiteral = def
540}
541
542func (option *Option) shortAndLongName() string {
543	ret := &bytes.Buffer{}
544
545	if option.ShortName != 0 {
546		ret.WriteRune(defaultShortOptDelimiter)
547		ret.WriteRune(option.ShortName)
548	}
549
550	if len(option.LongName) != 0 {
551		if option.ShortName != 0 {
552			ret.WriteRune('/')
553		}
554
555		ret.WriteString(option.LongName)
556	}
557
558	return ret.String()
559}
560
561func (option *Option) isValidValue(arg string) error {
562	if validator := option.isValueValidator(); validator != nil {
563		return validator.IsValidValue(arg)
564	}
565	if argumentIsOption(arg) && !(option.isSignedNumber() && len(arg) > 1 && arg[0] == '-' && arg[1] >= '0' && arg[1] <= '9') {
566		return fmt.Errorf("expected argument for flag `%s', but got option `%s'", option, arg)
567	}
568	return nil
569}
570