option.go 3.29 KB
Newer Older
Matt Bell's avatar
Matt Bell committed
1 2
package commands

3 4 5 6
import (
	"errors"
	"reflect"
)
Matt Bell's avatar
Matt Bell committed
7

8 9
var CastError = errors.New("cast error")

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
10
// Types of Command options
Matt Bell's avatar
Matt Bell committed
11
const (
Matt Bell's avatar
Matt Bell committed
12 13 14 15
	Invalid = reflect.Invalid
	Bool    = reflect.Bool
	Int     = reflect.Int
	Uint    = reflect.Uint
16
	Float   = reflect.Float64
Matt Bell's avatar
Matt Bell committed
17
	String  = reflect.String
Matt Bell's avatar
Matt Bell committed
18 19 20 21
)

// Option is used to specify a field that will be provided by a consumer
type Option struct {
22 23 24
	Names       []string     // a list of unique names to
	Type        reflect.Kind // value must be this type
	Description string       // a short string to describe this option
25

26
	// MAYBE_TODO: add more features(?):
Matt Bell's avatar
Matt Bell committed
27 28
	//Default interface{} // the default value (ignored if `Required` is true)
	//Required bool       // whether or not the option must be provided
Matt Bell's avatar
Matt Bell committed
29
}
30

31 32 33
// constructor helper functions
func NewOption(kind reflect.Kind, names ...string) Option {
	if len(names) < 2 {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
34
		// FIXME(btc) don't panic (fix_before_merge)
35 36 37 38
		panic("Options require at least two string values (name and description)")
	}

	desc := names[len(names)-1]
39
	names = names[:len(names)-1]
40 41 42 43 44 45 46 47

	return Option{
		Names:       names,
		Type:        kind,
		Description: desc,
	}
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
48 49 50 51 52 53
// TODO handle description separately. this will take care of the panic case in
// NewOption

// For all func {Type}Option(...string) functions, the last variadic argument
// is treated as the description field.

54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
func BoolOption(names ...string) Option {
	return NewOption(Bool, names...)
}
func IntOption(names ...string) Option {
	return NewOption(Int, names...)
}
func UintOption(names ...string) Option {
	return NewOption(Uint, names...)
}
func FloatOption(names ...string) Option {
	return NewOption(Float, names...)
}
func StringOption(names ...string) Option {
	return NewOption(String, names...)
}

70 71 72 73 74 75 76 77 78 79 80
type OptionValue struct {
	value interface{}
	found bool
}

// Found returns true if the option value was provided by the user (not a default value)
func (ov OptionValue) Found() bool {
	return ov.found
}

// value accessor methods, gets the value as a certain type
81 82 83 84
func (ov OptionValue) Bool() (value bool, found bool, err error) {
	if !ov.found {
		return false, false, nil
	}
85 86
	val, ok := ov.value.(bool)
	if !ok {
87
		err = CastError
88
	}
89
	return val, ov.found, err
90
}
91 92 93 94 95

func (ov OptionValue) Int() (val int, found bool, err error) {
	if !ov.found {
		return 0, false, nil
	}
96 97
	val, ok := ov.value.(int)
	if !ok {
98
		err = CastError
99
	}
100
	return val, ov.found, err
101
}
102 103 104 105 106

func (ov OptionValue) Uint() (val uint, found bool, err error) {
	if !ov.found {
		return 0, false, nil
	}
107 108
	val, ok := ov.value.(uint)
	if !ok {
109
		err = CastError
110
	}
111
	return val, ov.found, err
112
}
113 114 115 116 117

func (ov OptionValue) Float() (val float64, found bool, err error) {
	if !ov.found {
		return 0, false, nil
	}
118 119
	val, ok := ov.value.(float64)
	if !ok {
120
		err = CastError
121
	}
122
	return val, ov.found, err
123
}
124 125 126 127 128

func (ov OptionValue) String() (val string, found bool, err error) {
	if !ov.found {
		return "", false, nil
	}
129 130
	val, ok := ov.value.(string)
	if !ok {
131
		err = CastError
132
	}
133
	return val, ov.found, err
134 135
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
136 137 138 139 140 141
// Flag names
const (
	EncShort = "enc"
	EncLong  = "encoding"
)

142
// options that are used by this package
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
143
var globalOptions = []Option{
144 145
	Option{[]string{EncShort, EncLong}, String,
		"The encoding type the output should be encoded with (json, xml, or text)"},
146
}
Matt Bell's avatar
Matt Bell committed
147

148
// the above array of Options, wrapped in a Command
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
149
var globalCommand = &Command{
Matt Bell's avatar
Matt Bell committed
150
	Options: globalOptions,
151
}