request.go 3.53 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 32 33 34 35

	ConvertOptions(options map[string]Option) error
}

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

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

48 49 50 51
// 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
52 53
}

54 55 56 57 58 59 60 61 62
// 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
}

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

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

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

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

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

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

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

107
func (r *request) ConvertOptions(options map[string]Option) error {
108 109 110 111 112
	converted := make(map[string]interface{})

	for k, v := range r.options {
		opt, ok := options[k]
		if !ok {
113
			continue
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
		}

		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
143
			converted[name] = value
144 145 146 147 148 149 150
		}
	}

	r.options = converted
	return nil
}

151 152
// NewEmptyRequest initializes an empty request
func NewEmptyRequest() Request {
153
	return NewRequest(nil, nil, nil, nil)
Matt Bell's avatar
Matt Bell committed
154 155
}

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