package commands import ( "fmt" "reflect" "strings" "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) // Types of Command options const ( Invalid = reflect.Invalid Bool = reflect.Bool Int = reflect.Int Uint = reflect.Uint Float = reflect.Float64 String = reflect.String ) // Option is used to specify a field that will be provided by a consumer type Option interface { 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{} } type option struct { names []string kind reflect.Kind description string defaultVal interface{} } func (o *option) Names() []string { return o.names } func (o *option) Type() reflect.Kind { return o.kind } func (o *option) Description() string { if o.description[len(o.description)-1] != '.' { o.description += "." } if o.defaultVal != nil { if strings.Contains(o.description, "<>") { return strings.Replace(o.description, "<>", fmt.Sprintf("Default: %v.", o.defaultVal), -1) } else { return fmt.Sprintf("%s Default: %v.", o.description, o.defaultVal) } } return o.description } // constructor helper functions func NewOption(kind reflect.Kind, names ...string) Option { if len(names) < 2 { // FIXME(btc) don't panic (fix_before_merge) panic("Options require at least two string values (name and description)") } desc := names[len(names)-1] names = names[:len(names)-1] return &option{ names: names, kind: kind, description: desc, } } func (o *option) Default(v interface{}) Option { o.defaultVal = v return o } func (o *option) DefaultVal() interface{} { return o.defaultVal } // 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. 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...) } type OptionValue struct { value interface{} found bool def Option } // Found returns true if the option value was provided by the user (not a default value) func (ov OptionValue) Found() bool { return ov.found } // Definition returns the option definition for the provided value func (ov OptionValue) Definition() Option { return ov.def } // value accessor methods, gets the value as a certain type func (ov OptionValue) Bool() (value bool, found bool, err error) { if !ov.found && ov.value == nil { return false, false, nil } val, ok := ov.value.(bool) if !ok { err = util.ErrCast() } return val, ov.found, err } func (ov OptionValue) Int() (value int, found bool, err error) { if !ov.found && ov.value == nil { return 0, false, nil } val, ok := ov.value.(int) if !ok { err = util.ErrCast() } return val, ov.found, err } func (ov OptionValue) Uint() (value uint, found bool, err error) { if !ov.found && ov.value == nil { return 0, false, nil } val, ok := ov.value.(uint) if !ok { err = util.ErrCast() } return val, ov.found, err } func (ov OptionValue) Float() (value float64, found bool, err error) { if !ov.found && ov.value == nil { return 0, false, nil } val, ok := ov.value.(float64) if !ok { err = util.ErrCast() } return val, ov.found, err } func (ov OptionValue) String() (value string, found bool, err error) { if !ov.found && ov.value == nil { return "", false, nil } val, ok := ov.value.(string) if !ok { err = util.ErrCast() } return val, ov.found, err } // Flag names const ( EncShort = "enc" EncLong = "encoding" RecShort = "r" RecLong = "recursive" ChanOpt = "stream-channels" TimeoutOpt = "timeout" ) // options that are used by this package var OptionEncodingType = StringOption(EncLong, EncShort, "The encoding type the output should be encoded with (json, xml, or text)") var OptionRecursivePath = BoolOption(RecLong, RecShort, "Add directory paths recursively").Default(false) var OptionStreamChannels = BoolOption(ChanOpt, "Stream channel output") var OptionTimeout = StringOption(TimeoutOpt, "set a global timeout on the command") // global options, added to every command var globalOptions = []Option{ OptionEncodingType, OptionStreamChannels, OptionTimeout, } // the above array of Options, wrapped in a Command var globalCommand = &Command{ Options: globalOptions, }