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

3
import (
4
	"fmt"
5
	"reflect"
6
	"strings"
Matt Bell's avatar
Matt Bell committed
7

8
	"gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util"
9
)
10

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

// Option is used to specify a field that will be provided by a consumer
22
type Option interface {
23 24 25 26 27
	Names() []string            // a list of unique names matched with user-provided flags
	Type() reflect.Kind         // value must be this type
	Description() string        // a short string that describes this option
	Default(interface{}) Option // sets the default value of the option
	DefaultVal() interface{}
28 29 30 31 32 33
}

type option struct {
	names       []string
	kind        reflect.Kind
	description string
34
	defaultVal  interface{}
35 36 37 38 39 40 41 42 43
}

func (o *option) Names() []string {
	return o.names
}

func (o *option) Type() reflect.Kind {
	return o.kind
}
44

45
func (o *option) Description() string {
46 47 48 49
	if o.description[len(o.description)-1] != '.' {
		o.description += "."
	}
	if o.defaultVal != nil {
50 51
		if strings.Contains(o.description, "<<default>>") {
			return strings.Replace(o.description, "<<default>>",
52 53 54 55
				fmt.Sprintf("Default: %v.", o.defaultVal), -1)
		} else {
			return fmt.Sprintf("%s Default: %v.", o.description, o.defaultVal)
		}
56
	}
57
	return o.description
Matt Bell's avatar
Matt Bell committed
58
}
59

60 61 62
// constructor helper functions
func NewOption(kind reflect.Kind, names ...string) Option {
	if len(names) < 2 {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
63
		// FIXME(btc) don't panic (fix_before_merge)
64 65 66 67
		panic("Options require at least two string values (name and description)")
	}

	desc := names[len(names)-1]
68
	names = names[:len(names)-1]
69

70 71 72 73
	return &option{
		names:       names,
		kind:        kind,
		description: desc,
74 75 76
	}
}

77 78 79 80 81 82 83 84 85
func (o *option) Default(v interface{}) Option {
	o.defaultVal = v
	return o
}

func (o *option) DefaultVal() interface{} {
	return o.defaultVal
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
86 87 88 89 90 91
// 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.

92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
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...)
}

108 109 110
type OptionValue struct {
	value interface{}
	found bool
111
	def   Option
112 113 114 115 116 117 118
}

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

119 120 121 122 123
// Definition returns the option definition for the provided value
func (ov OptionValue) Definition() Option {
	return ov.def
}

124
// value accessor methods, gets the value as a certain type
125
func (ov OptionValue) Bool() (value bool, found bool, err error) {
126
	if !ov.found && ov.value == nil {
127 128
		return false, false, nil
	}
129 130
	val, ok := ov.value.(bool)
	if !ok {
131
		err = util.ErrCast()
132
	}
133
	return val, ov.found, err
134
}
135

136
func (ov OptionValue) Int() (value int, found bool, err error) {
137
	if !ov.found && ov.value == nil {
138 139
		return 0, false, nil
	}
140 141
	val, ok := ov.value.(int)
	if !ok {
142
		err = util.ErrCast()
143
	}
144
	return val, ov.found, err
145
}
146

147
func (ov OptionValue) Uint() (value uint, found bool, err error) {
148
	if !ov.found && ov.value == nil {
149 150
		return 0, false, nil
	}
151 152
	val, ok := ov.value.(uint)
	if !ok {
153
		err = util.ErrCast()
154
	}
155
	return val, ov.found, err
156
}
157

158
func (ov OptionValue) Float() (value float64, found bool, err error) {
159
	if !ov.found && ov.value == nil {
160 161
		return 0, false, nil
	}
162 163
	val, ok := ov.value.(float64)
	if !ok {
164
		err = util.ErrCast()
165
	}
166
	return val, ov.found, err
167
}
168

169
func (ov OptionValue) String() (value string, found bool, err error) {
170
	if !ov.found && ov.value == nil {
171 172
		return "", false, nil
	}
173 174
	val, ok := ov.value.(string)
	if !ok {
175
		err = util.ErrCast()
176
	}
177
	return val, ov.found, err
178 179
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
180 181
// Flag names
const (
182 183 184 185 186 187
	EncShort   = "enc"
	EncLong    = "encoding"
	RecShort   = "r"
	RecLong    = "recursive"
	ChanOpt    = "stream-channels"
	TimeoutOpt = "timeout"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
188 189
)

190
// options that are used by this package
rht's avatar
rht committed
191
var OptionEncodingType = StringOption(EncLong, EncShort, "The encoding type the output should be encoded with (json, xml, or text)")
192
var OptionRecursivePath = BoolOption(RecLong, RecShort, "Add directory paths recursively").Default(false)
193
var OptionStreamChannels = BoolOption(ChanOpt, "Stream channel output")
194
var OptionTimeout = StringOption(TimeoutOpt, "set a global timeout on the command")
195 196

// global options, added to every command
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
197
var globalOptions = []Option{
198
	OptionEncodingType,
199
	OptionStreamChannels,
200
	OptionTimeout,
201
}
Matt Bell's avatar
Matt Bell committed
202

203
// the above array of Options, wrapped in a Command
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
204
var globalCommand = &Command{
Matt Bell's avatar
Matt Bell committed
205
	Options: globalOptions,
206
}