request.go 3.72 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
	Command() *Command
33 34 35 36 37

	ConvertOptions(options map[string]Option) error
}

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

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

51 52 53 54
// 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
55 56
}

57 58 59 60 61 62 63 64 65
// 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
}

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

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

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

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

86 87 88 89
func (r *request) Context() *Context {
	return &r.ctx
}

90 91 92 93
func (r *request) SetContext(ctx Context) {
	r.ctx = ctx
}

94 95 96 97
func (r *request) Command() *Command {
	return r.cmd
}

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

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

118
func (r *request) ConvertOptions(options map[string]Option) error {
119 120 121 122 123
	converted := make(map[string]interface{})

	for k, v := range r.options {
		opt, ok := options[k]
		if !ok {
124
			continue
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
		}

		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
154
			converted[name] = value
155 156 157 158 159 160 161
		}
	}

	r.options = converted
	return nil
}

162 163
// NewEmptyRequest initializes an empty request
func NewEmptyRequest() Request {
164
	return NewRequest(nil, nil, nil, nil, nil)
Matt Bell's avatar
Matt Bell committed
165 166
}

167
// NewRequest returns a request initialized with given arguments
168
func NewRequest(path []string, opts optMap, args []string, in io.Reader, cmd *Command) Request {
169
	if path == nil {
Matt Bell's avatar
Matt Bell committed
170
		path = make([]string, 0)
Matt Bell's avatar
Matt Bell committed
171
	}
172 173 174 175 176 177
	if opts == nil {
		opts = make(map[string]interface{})
	}
	if args == nil {
		args = make([]string, 0)
	}
178
	return &request{path, opts, args, in, cmd, Context{}}
Matt Bell's avatar
Matt Bell committed
179
}