turned req + res into interfaces
Juan Batiz-Benet committed
Oct 20, 2014 at 11:49 UTC
b10fc2cc50f5cc05271b11b10ac41df0883f8d3c
6 files changed
+124
-71
commands/cli/parse.go
+1
-1
@@ -9,7 +9,7 @@ import (
9
10
// Parse parses the input commandline string (cmd, flags, and args).
11
// returns the corresponding command Request object.
12
-func Parse(input []string, root *commands.Command) (*commands.Request, error) {
12
+func Parse(input []string, root *commands.Command) (commands.Request, error) {
13
path, input := parsePath(input, root)
14
opts, args, err := parseOptions(input)
15
if err != nil {
commands/command.go
+10
-6
@@ -4,11 +4,15 @@ import (
4
"errors"
5
"fmt"
6
"strings"
7
+
8
+ u "github.com/jbenet/go-ipfs/util"
9
)
10
11
+var log = u.Logger("command")
12
+
13
// Function is the type of function that Commands use.
14
// It reads from the Request, and writes results to the Response.
11
-type Function func(*Request, *Response)
15
+type Function func(Request, Response)
16
17
// Command is a runnable command, with input arguments and options (flags).
18
// It can also have subcommands, to group units of work into sets.
@@ -43,10 +47,10 @@ func (c *Command) Register(id string, sub *Command) error {
47
}
48
49
// Call invokes the command at the given subcommand path
46
-func (c *Command) Call(req *Request) *Response {
47
- res := &Response{req: req}
50
+func (c *Command) Call(req Request) Response {
51
+ res := NewResponse(req)
52
49
- cmds, err := c.Resolve(req.path)
53
+ cmds, err := c.Resolve(req.Path())
54
if err != nil {
55
res.SetError(err, ErrClient)
56
return res
@@ -58,13 +62,13 @@ func (c *Command) Call(req *Request) *Response {
62
return res
63
}
64
61
- options, err := c.GetOptions(req.path)
65
+ options, err := c.GetOptions(req.Path())
66
if err != nil {
67
res.SetError(err, ErrClient)
68
return res
69
}
70
67
- err = req.convertOptions(options)
71
+ err = req.ConvertOptions(options)
72
if err != nil {
73
res.SetError(err, ErrClient)
74
return res
commands/command_test.go
+28
-27
@@ -8,70 +8,70 @@ func TestOptionValidation(t *testing.T) {
8
Option{[]string{"b", "beep"}, Int},
9
Option{[]string{"B", "boop"}, String},
10
},
11
- run: func(req *Request, res *Response) {},
11
+ run: func(req Request, res Response) {},
12
}
13
14
req := NewEmptyRequest()
15
- req.options["foo"] = 5
15
+ req.SetOption("foo", 5)
16
res := cmd.Call(req)
17
- if res.Error == nil {
17
+ if res.Error() == nil {
18
t.Error("Should have failed (unrecognized option)")
19
}
20
21
req = NewEmptyRequest()
22
- req.options["beep"] = 5
23
- req.options["b"] = 10
22
+ req.SetOption("beep", 5)
23
+ req.SetOption("b", 10)
24
res = cmd.Call(req)
25
- if res.Error == nil {
25
+ if res.Error() == nil {
26
t.Error("Should have failed (duplicate options)")
27
}
28
29
req = NewEmptyRequest()
30
- req.options["beep"] = "foo"
30
+ req.SetOption("beep", "foo")
31
res = cmd.Call(req)
32
- if res.Error == nil {
32
+ if res.Error() == nil {
33
t.Error("Should have failed (incorrect type)")
34
}
35
36
req = NewEmptyRequest()
37
- req.options["beep"] = 5
37
+ req.SetOption("beep", 5)
38
res = cmd.Call(req)
39
- if res.Error != nil {
40
- t.Error(res.Error, "Should have passed")
39
+ if res.Error() != nil {
40
+ t.Error(res.Error(), "Should have passed")
41
}
42
43
req = NewEmptyRequest()
44
- req.options["beep"] = 5
45
- req.options["boop"] = "test"
44
+ req.SetOption("beep", 5)
45
+ req.SetOption("boop", "test")
46
res = cmd.Call(req)
47
- if res.Error != nil {
47
+ if res.Error() != nil {
48
t.Error("Should have passed")
49
}
50
51
req = NewEmptyRequest()
52
- req.options["b"] = 5
53
- req.options["B"] = "test"
52
+ req.SetOption("b", 5)
53
+ req.SetOption("B", "test")
54
res = cmd.Call(req)
55
- if res.Error != nil {
55
+ if res.Error() != nil {
56
t.Error("Should have passed")
57
}
58
59
req = NewEmptyRequest()
60
- req.options[EncShort] = "json"
60
+ req.SetOption(EncShort, "json")
61
res = cmd.Call(req)
62
- if res.Error != nil {
62
+ if res.Error() != nil {
63
t.Error("Should have passed")
64
}
65
66
req = NewEmptyRequest()
67
- req.options["b"] = "100"
67
+ req.SetOption("b", "100")
68
res = cmd.Call(req)
69
- if res.Error != nil {
69
+ if res.Error() != nil {
70
t.Error("Should have passed")
71
}
72
73
req = NewEmptyRequest()
74
- req.options["b"] = ":)"
74
+ req.SetOption("b", ":)")
75
res = cmd.Call(req)
76
if res.Error == nil {
77
t.Error(res.Error, "Should have failed (string value not convertible to int)")
@@ -79,40 +79,41 @@ func TestOptionValidation(t *testing.T) {
79
}
80
81
func TestRegistration(t *testing.T) {
82
+ noop := func(req Request, res Response) {}
83
cmds := []*Command{
84
&Command{
85
Options: []Option{
86
Option{[]string{"beep"}, Int},
87
},
87
- run: func(req *Request, res *Response) {},
88
+ run: noop,
89
},
90
91
&Command{
92
Options: []Option{
93
Option{[]string{"boop"}, Int},
94
},
94
- run: func(req *Request, res *Response) {},
95
+ run: noop,
96
},
97
98
&Command{
99
Options: []Option{
100
Option{[]string{"boop"}, String},
101
},
101
- run: func(req *Request, res *Response) {},
102
+ run: noop,
103
},
104
105
&Command{
106
Options: []Option{
107
Option{[]string{"bop"}, String},
108
},
108
- run: func(req *Request, res *Response) {},
109
+ run: noop,
110
},
111
112
&Command{
113
Options: []Option{
114
Option{[]string{EncShort}, String},
115
},
115
- run: func(req *Request, res *Response) {},
116
+ run: noop,
117
},
118
}
119
commands/request.go
+31
-18
@@ -6,37 +6,48 @@ import (
6
"strconv"
7
)
8
9
+type optMap map[string]interface{}
10
+
11
// Request represents a call to a command from a consumer
10
-type Request struct {
12
+type Request interface {
13
+ Path() []string
14
+ Option(name string) (interface{}, bool)
15
+ SetOption(name string, val interface{})
16
+ Arguments() []string
17
+
18
+ ConvertOptions(options map[string]Option) error
19
+}
20
+
21
+type request struct {
22
path []string
12
- options map[string]interface{}
23
+ options optMap
24
arguments []string
25
}
26
16
-func (r *Request) Path() []string {
27
+// Path returns the command path of this request
28
+func (r *request) Path() []string {
29
return r.path
30
}
31
20
-func (r *Request) SetPath(path []string) {
21
- r.path = path
22
-}
23
-
24
-func (r *Request) Option(name string) (interface{}, bool) {
25
- val, ok := r.options[name]
26
- return val, ok
32
+// Option returns the value of the option for given name.
33
+func (r *request) Option(name string) (interface{}, bool) {
34
+ val, err := r.options[name]
35
+ return val, err
36
}
37
29
-func (r *Request) SetOption(name string, value interface{}) {
30
- r.options[name] = value
38
+// SetOption sets the value of the option for given name.
39
+func (r *request) SetOption(name string, val interface{}) {
40
+ r.options[name] = val
41
}
42
33
-func (r *Request) Arguments() []string {
43
+// Arguments returns the arguments slice
44
+func (r *request) Arguments() []string {
45
return r.arguments
46
}
47
48
type converter func(string) (interface{}, error)
49
39
-var converters map[reflect.Kind]converter = map[reflect.Kind]converter{
50
+var converters = map[reflect.Kind]converter{
51
Bool: func(v string) (interface{}, error) {
52
if v == "" {
53
return true, nil
@@ -54,7 +65,7 @@ var converters map[reflect.Kind]converter = map[reflect.Kind]converter{
65
},
66
}
67
57
-func (r *Request) convertOptions(options map[string]Option) error {
68
+func (r *request) ConvertOptions(options map[string]Option) error {
69
converted := make(map[string]interface{})
70
71
for k, v := range r.options {
@@ -98,11 +109,13 @@ func (r *Request) convertOptions(options map[string]Option) error {
109
return nil
110
}
111
101
-func NewEmptyRequest() *Request {
112
+// NewEmptyRequest initializes an empty request
113
+func NewEmptyRequest() Request {
114
return NewRequest(nil, nil, nil)
115
}
116
105
-func NewRequest(path []string, opts map[string]interface{}, args []string) *Request {
117
+// NewRequest returns a request initialized with given arguments
118
+func NewRequest(path []string, opts optMap, args []string) Request {
119
if path == nil {
120
path = make([]string, 0)
121
}
@@ -112,5 +125,5 @@ func NewRequest(path []string, opts map[string]interface{}, args []string) *Requ
125
if args == nil {
126
args = make([]string, 0)
127
}
115
- return &Request{path, opts, args}
128
+ return &request{path, opts, args}
129
}
commands/response.go
+51
-14
@@ -48,21 +48,53 @@ var marshallers = map[EncodingType]Marshaller{
48
49
// Response is the result of a command request. Handlers write to the response,
50
// setting Error or Value. Response is returned to the client.
51
-type Response struct {
52
- req *Request
53
- Error *Error
54
- Value interface{}
51
+type Response interface {
52
+ Request() Request
53
+
54
+ // Set/Return the response Error
55
+ SetError(err error, code ErrorType)
56
+ Error() error
57
+
58
+ // Sets/Returns the response value
59
+ SetValue(interface{})
60
+ Value() interface{}
61
+
62
+ // Marshal marshals out the response into a buffer. It uses the EncodingType
63
+ // on the Request to chose a Marshaller (Codec).
64
+ Marshal() ([]byte, error)
65
+}
66
+
67
+type response struct {
68
+ req Request
69
+ err *Error
70
+ value interface{}
71
+}
72
+
73
+func (r *response) Request() Request {
74
+ return r.req
75
+}
76
+
77
+func (r *response) Value() interface{} {
78
+ return r.value
79
+}
80
+
81
+func (r *response) SetValue(v interface{}) {
82
+ r.value = v
83
+}
84
+
85
+func (r *response) Error() error {
86
+ if r.err == nil {
87
+ return nil
88
+ }
89
+ return r.err
90
}
91
57
-// SetError updates the response Error.
58
-func (r *Response) SetError(err error, code ErrorType) {
59
- r.Error = &Error{Message: err.Error(), Code: code}
92
+func (r *response) SetError(err error, code ErrorType) {
93
+ r.err = &Error{Message: err.Error(), Code: code}
94
}
95
62
-// Marshal marshals out the response into a buffer. It uses the EncodingType
63
-// on the Request to chose a Marshaller (Codec).
64
-func (r *Response) Marshal() ([]byte, error) {
65
- if r.Error == nil && r.Value == nil {
96
+func (r *response) Marshal() ([]byte, error) {
97
+ if r.err == nil && r.value == nil {
98
return nil, fmt.Errorf("No error or value set, there is nothing to marshal")
99
}
100
@@ -77,8 +109,13 @@ func (r *Response) Marshal() ([]byte, error) {
109
return nil, fmt.Errorf("No marshaller found for encoding type '%s'", enc)
110
}
111
80
- if r.Error != nil {
81
- return marshaller(r.Error)
112
+ if r.err != nil {
113
+ return marshaller(r.err)
114
}
83
- return marshaller(r.Value)
115
+ return marshaller(r.value)
116
+}
117
+
118
+// NewResponse returns a response to match given Request
119
+func NewResponse(req Request) Response {
120
+ return &response{req: req}
121
}
commands/response_test.go
+3
-5
@@ -13,10 +13,8 @@ type TestOutput struct {
13
func TestMarshalling(t *testing.T) {
14
req := NewEmptyRequest()
15
16
- res := Response{
17
- req: req,
18
- Value: TestOutput{"beep", "boop", 1337},
19
- }
16
+ res := NewResponse(req)
17
+ res.SetValue(TestOutput{"beep", "boop", 1337})
18
19
// get command global options so we can set the encoding option
20
cmd := Command{}
@@ -31,7 +29,7 @@ func TestMarshalling(t *testing.T) {
29
}
30
31
req.SetOption(EncShort, JSON)
34
- req.convertOptions(options)
32
+ req.ConvertOptions(options)
33
34
bytes, err := res.Marshal()
35
if err != nil {