Commit b10fc2cc authored by Juan Batiz-Benet's avatar Juan Batiz-Benet

turned req + res into interfaces

parent bbef82f4
......@@ -9,7 +9,7 @@ import (
// Parse parses the input commandline string (cmd, flags, and args).
// returns the corresponding command Request object.
func Parse(input []string, root *commands.Command) (*commands.Request, error) {
func Parse(input []string, root *commands.Command) (commands.Request, error) {
path, input := parsePath(input, root)
opts, args, err := parseOptions(input)
if err != nil {
......
......@@ -4,11 +4,15 @@ import (
"errors"
"fmt"
"strings"
u "github.com/jbenet/go-ipfs/util"
)
var log = u.Logger("command")
// Function is the type of function that Commands use.
// It reads from the Request, and writes results to the Response.
type Function func(*Request, *Response)
type Function func(Request, Response)
// Command is a runnable command, with input arguments and options (flags).
// 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 {
}
// Call invokes the command at the given subcommand path
func (c *Command) Call(req *Request) *Response {
res := &Response{req: req}
func (c *Command) Call(req Request) Response {
res := NewResponse(req)
cmds, err := c.Resolve(req.path)
cmds, err := c.Resolve(req.Path())
if err != nil {
res.SetError(err, ErrClient)
return res
......@@ -58,13 +62,13 @@ func (c *Command) Call(req *Request) *Response {
return res
}
options, err := c.GetOptions(req.path)
options, err := c.GetOptions(req.Path())
if err != nil {
res.SetError(err, ErrClient)
return res
}
err = req.convertOptions(options)
err = req.ConvertOptions(options)
if err != nil {
res.SetError(err, ErrClient)
return res
......
......@@ -8,70 +8,70 @@ func TestOptionValidation(t *testing.T) {
Option{[]string{"b", "beep"}, Int},
Option{[]string{"B", "boop"}, String},
},
run: func(req *Request, res *Response) {},
run: func(req Request, res Response) {},
}
req := NewEmptyRequest()
req.options["foo"] = 5
req.SetOption("foo", 5)
res := cmd.Call(req)
if res.Error == nil {
if res.Error() == nil {
t.Error("Should have failed (unrecognized option)")
}
req = NewEmptyRequest()
req.options["beep"] = 5
req.options["b"] = 10
req.SetOption("beep", 5)
req.SetOption("b", 10)
res = cmd.Call(req)
if res.Error == nil {
if res.Error() == nil {
t.Error("Should have failed (duplicate options)")
}
req = NewEmptyRequest()
req.options["beep"] = "foo"
req.SetOption("beep", "foo")
res = cmd.Call(req)
if res.Error == nil {
if res.Error() == nil {
t.Error("Should have failed (incorrect type)")
}
req = NewEmptyRequest()
req.options["beep"] = 5
req.SetOption("beep", 5)
res = cmd.Call(req)
if res.Error != nil {
t.Error(res.Error, "Should have passed")
if res.Error() != nil {
t.Error(res.Error(), "Should have passed")
}
req = NewEmptyRequest()
req.options["beep"] = 5
req.options["boop"] = "test"
req.SetOption("beep", 5)
req.SetOption("boop", "test")
res = cmd.Call(req)
if res.Error != nil {
if res.Error() != nil {
t.Error("Should have passed")
}
req = NewEmptyRequest()
req.options["b"] = 5
req.options["B"] = "test"
req.SetOption("b", 5)
req.SetOption("B", "test")
res = cmd.Call(req)
if res.Error != nil {
if res.Error() != nil {
t.Error("Should have passed")
}
req = NewEmptyRequest()
req.options[EncShort] = "json"
req.SetOption(EncShort, "json")
res = cmd.Call(req)
if res.Error != nil {
if res.Error() != nil {
t.Error("Should have passed")
}
req = NewEmptyRequest()
req.options["b"] = "100"
req.SetOption("b", "100")
res = cmd.Call(req)
if res.Error != nil {
if res.Error() != nil {
t.Error("Should have passed")
}
req = NewEmptyRequest()
req.options["b"] = ":)"
req.SetOption("b", ":)")
res = cmd.Call(req)
if res.Error == nil {
t.Error(res.Error, "Should have failed (string value not convertible to int)")
......@@ -79,40 +79,41 @@ func TestOptionValidation(t *testing.T) {
}
func TestRegistration(t *testing.T) {
noop := func(req Request, res Response) {}
cmds := []*Command{
&Command{
Options: []Option{
Option{[]string{"beep"}, Int},
},
run: func(req *Request, res *Response) {},
run: noop,
},
&Command{
Options: []Option{
Option{[]string{"boop"}, Int},
},
run: func(req *Request, res *Response) {},
run: noop,
},
&Command{
Options: []Option{
Option{[]string{"boop"}, String},
},
run: func(req *Request, res *Response) {},
run: noop,
},
&Command{
Options: []Option{
Option{[]string{"bop"}, String},
},
run: func(req *Request, res *Response) {},
run: noop,
},
&Command{
Options: []Option{
Option{[]string{EncShort}, String},
},
run: func(req *Request, res *Response) {},
run: noop,
},
}
......
......@@ -6,37 +6,48 @@ import (
"strconv"
)
type optMap map[string]interface{}
// Request represents a call to a command from a consumer
type Request struct {
type Request interface {
Path() []string
Option(name string) (interface{}, bool)
SetOption(name string, val interface{})
Arguments() []string
ConvertOptions(options map[string]Option) error
}
type request struct {
path []string
options map[string]interface{}
options optMap
arguments []string
}
func (r *Request) Path() []string {
// Path returns the command path of this request
func (r *request) Path() []string {
return r.path
}
func (r *Request) SetPath(path []string) {
r.path = path
}
func (r *Request) Option(name string) (interface{}, bool) {
val, ok := r.options[name]
return val, ok
// 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
}
func (r *Request) SetOption(name string, value interface{}) {
r.options[name] = value
// SetOption sets the value of the option for given name.
func (r *request) SetOption(name string, val interface{}) {
r.options[name] = val
}
func (r *Request) Arguments() []string {
// Arguments returns the arguments slice
func (r *request) Arguments() []string {
return r.arguments
}
type converter func(string) (interface{}, error)
var converters map[reflect.Kind]converter = map[reflect.Kind]converter{
var converters = map[reflect.Kind]converter{
Bool: func(v string) (interface{}, error) {
if v == "" {
return true, nil
......@@ -54,7 +65,7 @@ var converters map[reflect.Kind]converter = map[reflect.Kind]converter{
},
}
func (r *Request) convertOptions(options map[string]Option) error {
func (r *request) ConvertOptions(options map[string]Option) error {
converted := make(map[string]interface{})
for k, v := range r.options {
......@@ -98,11 +109,13 @@ func (r *Request) convertOptions(options map[string]Option) error {
return nil
}
func NewEmptyRequest() *Request {
// NewEmptyRequest initializes an empty request
func NewEmptyRequest() Request {
return NewRequest(nil, nil, nil)
}
func NewRequest(path []string, opts map[string]interface{}, args []string) *Request {
// NewRequest returns a request initialized with given arguments
func NewRequest(path []string, opts optMap, args []string) Request {
if path == nil {
path = make([]string, 0)
}
......@@ -112,5 +125,5 @@ func NewRequest(path []string, opts map[string]interface{}, args []string) *Requ
if args == nil {
args = make([]string, 0)
}
return &Request{path, opts, args}
return &request{path, opts, args}
}
......@@ -48,21 +48,53 @@ var marshallers = map[EncodingType]Marshaller{
// Response is the result of a command request. Handlers write to the response,
// setting Error or Value. Response is returned to the client.
type Response struct {
req *Request
Error *Error
Value interface{}
type Response interface {
Request() Request
// Set/Return the response Error
SetError(err error, code ErrorType)
Error() error
// Sets/Returns the response value
SetValue(interface{})
Value() interface{}
// Marshal marshals out the response into a buffer. It uses the EncodingType
// on the Request to chose a Marshaller (Codec).
Marshal() ([]byte, error)
}
type response struct {
req Request
err *Error
value interface{}
}
func (r *response) Request() Request {
return r.req
}
func (r *response) Value() interface{} {
return r.value
}
func (r *response) SetValue(v interface{}) {
r.value = v
}
func (r *response) Error() error {
if r.err == nil {
return nil
}
return r.err
}
// SetError updates the response Error.
func (r *Response) SetError(err error, code ErrorType) {
r.Error = &Error{Message: err.Error(), Code: code}
func (r *response) SetError(err error, code ErrorType) {
r.err = &Error{Message: err.Error(), Code: code}
}
// Marshal marshals out the response into a buffer. It uses the EncodingType
// on the Request to chose a Marshaller (Codec).
func (r *Response) Marshal() ([]byte, error) {
if r.Error == nil && r.Value == nil {
func (r *response) Marshal() ([]byte, error) {
if r.err == nil && r.value == nil {
return nil, fmt.Errorf("No error or value set, there is nothing to marshal")
}
......@@ -77,8 +109,13 @@ func (r *Response) Marshal() ([]byte, error) {
return nil, fmt.Errorf("No marshaller found for encoding type '%s'", enc)
}
if r.Error != nil {
return marshaller(r.Error)
if r.err != nil {
return marshaller(r.err)
}
return marshaller(r.Value)
return marshaller(r.value)
}
// NewResponse returns a response to match given Request
func NewResponse(req Request) Response {
return &response{req: req}
}
......@@ -13,10 +13,8 @@ type TestOutput struct {
func TestMarshalling(t *testing.T) {
req := NewEmptyRequest()
res := Response{
req: req,
Value: TestOutput{"beep", "boop", 1337},
}
res := NewResponse(req)
res.SetValue(TestOutput{"beep", "boop", 1337})
// get command global options so we can set the encoding option
cmd := Command{}
......@@ -31,7 +29,7 @@ func TestMarshalling(t *testing.T) {
}
req.SetOption(EncShort, JSON)
req.convertOptions(options)
req.ConvertOptions(options)
bytes, err := res.Marshal()
if err != nil {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment