request.go 3.61 KB
Newer Older
Matt Bell's avatar
Matt Bell committed
1 2
package commands

3 4
import (
	"fmt"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
5
	"io"
6 7
	"reflect"
	"strconv"
8 9 10

	"github.com/jbenet/go-ipfs/config"
	"github.com/jbenet/go-ipfs/core"
11 12
)

13 14
type optMap map[string]interface{}

15 16 17 18 19 20
type Context struct {
	ConfigRoot string
	Config     *config.Config
	Node       *core.IpfsNode
}

Matt Bell's avatar
Matt Bell committed
21
// Request represents a call to a command from a consumer
22 23 24
type Request interface {
	Path() []string
	Option(name string) (interface{}, bool)
25
	Options() map[string]interface{}
26 27
	SetOption(name string, val interface{})
	Arguments() []string
28
	Stream() io.Reader
Matt Bell's avatar
Matt Bell committed
29
	SetStream(io.Reader)
30
	Context() *Context
31
	SetContext(Context)
32 33 34 35 36

	ConvertOptions(options map[string]Option) error
}

type request struct {
Matt Bell's avatar
Matt Bell committed
37
	path      []string
38
	options   optMap
Matt Bell's avatar
Matt Bell committed
39
	arguments []string
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
40
	in        io.Reader
41
	ctx       Context
Matt Bell's avatar
Matt Bell committed
42 43
}

44 45
// Path returns the command path of this request
func (r *request) Path() []string {
46 47 48
	return r.path
}

49 50 51 52
// Option returns the value of the option for given name.
func (r *request) Option(name string) (interface{}, bool) {
	val, err := r.options[name]
	return val, err
Matt Bell's avatar
Matt Bell committed
53 54
}

55 56 57 58 59 60 61 62 63
// Options returns a copy of the option map
func (r *request) Options() map[string]interface{} {
	output := make(optMap)
	for k, v := range r.options {
		output[k] = v
	}
	return output
}

64 65 66
// SetOption sets the value of the option for given name.
func (r *request) SetOption(name string, val interface{}) {
	r.options[name] = val
67 68
}

69 70
// Arguments returns the arguments slice
func (r *request) Arguments() []string {
Matt Bell's avatar
Matt Bell committed
71
	return r.arguments
72
}
Matt Bell's avatar
Matt Bell committed
73

74 75 76 77 78
// Stream returns the input stream Reader
func (r *request) Stream() io.Reader {
	return r.in
}

Matt Bell's avatar
Matt Bell committed
79 80 81 82 83
// SetStream sets the value of the input stream Reader
func (r *request) SetStream(in io.Reader) {
	r.in = in
}

84 85 86 87
func (r *request) Context() *Context {
	return &r.ctx
}

88 89 90 91
func (r *request) SetContext(ctx Context) {
	r.ctx = ctx
}

Matt Bell's avatar
Matt Bell committed
92 93
type converter func(string) (interface{}, error)

94
var converters = map[reflect.Kind]converter{
Matt Bell's avatar
Matt Bell committed
95
	Bool: func(v string) (interface{}, error) {
96 97 98 99 100
		if v == "" {
			return true, nil
		}
		return strconv.ParseBool(v)
	},
Matt Bell's avatar
Matt Bell committed
101
	Int: func(v string) (interface{}, error) {
102 103
		return strconv.ParseInt(v, 0, 32)
	},
Matt Bell's avatar
Matt Bell committed
104
	Uint: func(v string) (interface{}, error) {
105 106
		return strconv.ParseInt(v, 0, 32)
	},
Matt Bell's avatar
Matt Bell committed
107
	Float: func(v string) (interface{}, error) {
108 109 110 111
		return strconv.ParseFloat(v, 64)
	},
}

112
func (r *request) ConvertOptions(options map[string]Option) error {
113 114 115 116 117
	converted := make(map[string]interface{})

	for k, v := range r.options {
		opt, ok := options[k]
		if !ok {
118
			continue
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
		}

		kind := reflect.TypeOf(v).Kind()
		var value interface{}

		if kind != opt.Type {
			if kind == String {
				convert := converters[opt.Type]
				val, err := convert(v.(string))
				if err != nil {
					return fmt.Errorf("Could not convert string value '%s' to type '%s'",
						v, opt.Type.String())
				}
				value = val

			} else {
				return fmt.Errorf("Option '%s' should be type '%s', but got type '%s'",
					k, opt.Type.String(), kind.String())
			}
		} else {
			value = v
		}

		for _, name := range opt.Names {
			if _, ok := r.options[name]; name != k && ok {
				return fmt.Errorf("Duplicate command options were provided ('%s' and '%s')",
					k, name)
			}

Matt Bell's avatar
Matt Bell committed
148
			converted[name] = value
149 150 151 152 153 154 155
		}
	}

	r.options = converted
	return nil
}

156 157
// NewEmptyRequest initializes an empty request
func NewEmptyRequest() Request {
158
	return NewRequest(nil, nil, nil, nil)
Matt Bell's avatar
Matt Bell committed
159 160
}

161
// NewRequest returns a request initialized with given arguments
162
func NewRequest(path []string, opts optMap, args []string, in io.Reader) Request {
163
	if path == nil {
Matt Bell's avatar
Matt Bell committed
164
		path = make([]string, 0)
Matt Bell's avatar
Matt Bell committed
165
	}
166 167 168 169 170 171
	if opts == nil {
		opts = make(map[string]interface{})
	}
	if args == nil {
		args = make([]string, 0)
	}
172
	return &request{path, opts, args, in, Context{}}
Matt Bell's avatar
Matt Bell committed
173
}