1
0
mirror of https://github.com/ipfs/kubo.git synced 2025-05-22 01:17:11 +08:00

turned req + res into interfaces

This commit is contained in:
Juan Batiz-Benet
2014-10-20 11:49:07 -07:00
parent bbef82f4fa
commit b10fc2cc50
6 changed files with 124 additions and 71 deletions

View File

@ -9,7 +9,7 @@ import (
// Parse parses the input commandline string (cmd, flags, and args). // Parse parses the input commandline string (cmd, flags, and args).
// returns the corresponding command Request object. // 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) path, input := parsePath(input, root)
opts, args, err := parseOptions(input) opts, args, err := parseOptions(input)
if err != nil { if err != nil {

View File

@ -4,11 +4,15 @@ import (
"errors" "errors"
"fmt" "fmt"
"strings" "strings"
u "github.com/jbenet/go-ipfs/util"
) )
var log = u.Logger("command")
// Function is the type of function that Commands use. // Function is the type of function that Commands use.
// It reads from the Request, and writes results to the Response. // 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). // Command is a runnable command, with input arguments and options (flags).
// It can also have subcommands, to group units of work into sets. // 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 // Call invokes the command at the given subcommand path
func (c *Command) Call(req *Request) *Response { func (c *Command) Call(req Request) Response {
res := &Response{req: req} res := NewResponse(req)
cmds, err := c.Resolve(req.path) cmds, err := c.Resolve(req.Path())
if err != nil { if err != nil {
res.SetError(err, ErrClient) res.SetError(err, ErrClient)
return res return res
@ -58,13 +62,13 @@ func (c *Command) Call(req *Request) *Response {
return res return res
} }
options, err := c.GetOptions(req.path) options, err := c.GetOptions(req.Path())
if err != nil { if err != nil {
res.SetError(err, ErrClient) res.SetError(err, ErrClient)
return res return res
} }
err = req.convertOptions(options) err = req.ConvertOptions(options)
if err != nil { if err != nil {
res.SetError(err, ErrClient) res.SetError(err, ErrClient)
return res return res

View File

@ -8,70 +8,70 @@ func TestOptionValidation(t *testing.T) {
Option{[]string{"b", "beep"}, Int}, Option{[]string{"b", "beep"}, Int},
Option{[]string{"B", "boop"}, String}, Option{[]string{"B", "boop"}, String},
}, },
run: func(req *Request, res *Response) {}, run: func(req Request, res Response) {},
} }
req := NewEmptyRequest() req := NewEmptyRequest()
req.options["foo"] = 5 req.SetOption("foo", 5)
res := cmd.Call(req) res := cmd.Call(req)
if res.Error == nil { if res.Error() == nil {
t.Error("Should have failed (unrecognized option)") t.Error("Should have failed (unrecognized option)")
} }
req = NewEmptyRequest() req = NewEmptyRequest()
req.options["beep"] = 5 req.SetOption("beep", 5)
req.options["b"] = 10 req.SetOption("b", 10)
res = cmd.Call(req) res = cmd.Call(req)
if res.Error == nil { if res.Error() == nil {
t.Error("Should have failed (duplicate options)") t.Error("Should have failed (duplicate options)")
} }
req = NewEmptyRequest() req = NewEmptyRequest()
req.options["beep"] = "foo" req.SetOption("beep", "foo")
res = cmd.Call(req) res = cmd.Call(req)
if res.Error == nil { if res.Error() == nil {
t.Error("Should have failed (incorrect type)") t.Error("Should have failed (incorrect type)")
} }
req = NewEmptyRequest() req = NewEmptyRequest()
req.options["beep"] = 5 req.SetOption("beep", 5)
res = cmd.Call(req) res = cmd.Call(req)
if res.Error != nil { if res.Error() != nil {
t.Error(res.Error, "Should have passed") t.Error(res.Error(), "Should have passed")
} }
req = NewEmptyRequest() req = NewEmptyRequest()
req.options["beep"] = 5 req.SetOption("beep", 5)
req.options["boop"] = "test" req.SetOption("boop", "test")
res = cmd.Call(req) res = cmd.Call(req)
if res.Error != nil { if res.Error() != nil {
t.Error("Should have passed") t.Error("Should have passed")
} }
req = NewEmptyRequest() req = NewEmptyRequest()
req.options["b"] = 5 req.SetOption("b", 5)
req.options["B"] = "test" req.SetOption("B", "test")
res = cmd.Call(req) res = cmd.Call(req)
if res.Error != nil { if res.Error() != nil {
t.Error("Should have passed") t.Error("Should have passed")
} }
req = NewEmptyRequest() req = NewEmptyRequest()
req.options[EncShort] = "json" req.SetOption(EncShort, "json")
res = cmd.Call(req) res = cmd.Call(req)
if res.Error != nil { if res.Error() != nil {
t.Error("Should have passed") t.Error("Should have passed")
} }
req = NewEmptyRequest() req = NewEmptyRequest()
req.options["b"] = "100" req.SetOption("b", "100")
res = cmd.Call(req) res = cmd.Call(req)
if res.Error != nil { if res.Error() != nil {
t.Error("Should have passed") t.Error("Should have passed")
} }
req = NewEmptyRequest() req = NewEmptyRequest()
req.options["b"] = ":)" req.SetOption("b", ":)")
res = cmd.Call(req) res = cmd.Call(req)
if res.Error == nil { if res.Error == nil {
t.Error(res.Error, "Should have failed (string value not convertible to int)") 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) { func TestRegistration(t *testing.T) {
noop := func(req Request, res Response) {}
cmds := []*Command{ cmds := []*Command{
&Command{ &Command{
Options: []Option{ Options: []Option{
Option{[]string{"beep"}, Int}, Option{[]string{"beep"}, Int},
}, },
run: func(req *Request, res *Response) {}, run: noop,
}, },
&Command{ &Command{
Options: []Option{ Options: []Option{
Option{[]string{"boop"}, Int}, Option{[]string{"boop"}, Int},
}, },
run: func(req *Request, res *Response) {}, run: noop,
}, },
&Command{ &Command{
Options: []Option{ Options: []Option{
Option{[]string{"boop"}, String}, Option{[]string{"boop"}, String},
}, },
run: func(req *Request, res *Response) {}, run: noop,
}, },
&Command{ &Command{
Options: []Option{ Options: []Option{
Option{[]string{"bop"}, String}, Option{[]string{"bop"}, String},
}, },
run: func(req *Request, res *Response) {}, run: noop,
}, },
&Command{ &Command{
Options: []Option{ Options: []Option{
Option{[]string{EncShort}, String}, Option{[]string{EncShort}, String},
}, },
run: func(req *Request, res *Response) {}, run: noop,
}, },
} }

View File

@ -6,37 +6,48 @@ import (
"strconv" "strconv"
) )
type optMap map[string]interface{}
// Request represents a call to a command from a consumer // 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 path []string
options map[string]interface{} options optMap
arguments []string arguments []string
} }
func (r *Request) Path() []string { // Path returns the command path of this request
func (r *request) Path() []string {
return r.path return r.path
} }
func (r *Request) SetPath(path []string) { // Option returns the value of the option for given name.
r.path = path func (r *request) Option(name string) (interface{}, bool) {
val, err := r.options[name]
return val, err
} }
func (r *Request) Option(name string) (interface{}, bool) { // SetOption sets the value of the option for given name.
val, ok := r.options[name] func (r *request) SetOption(name string, val interface{}) {
return val, ok r.options[name] = val
} }
func (r *Request) SetOption(name string, value interface{}) { // Arguments returns the arguments slice
r.options[name] = value func (r *request) Arguments() []string {
}
func (r *Request) Arguments() []string {
return r.arguments return r.arguments
} }
type converter func(string) (interface{}, error) 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) { Bool: func(v string) (interface{}, error) {
if v == "" { if v == "" {
return true, nil 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{}) converted := make(map[string]interface{})
for k, v := range r.options { for k, v := range r.options {
@ -98,11 +109,13 @@ func (r *Request) convertOptions(options map[string]Option) error {
return nil return nil
} }
func NewEmptyRequest() *Request { // NewEmptyRequest initializes an empty request
func NewEmptyRequest() Request {
return NewRequest(nil, nil, nil) 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 { if path == nil {
path = make([]string, 0) path = make([]string, 0)
} }
@ -112,5 +125,5 @@ func NewRequest(path []string, opts map[string]interface{}, args []string) *Requ
if args == nil { if args == nil {
args = make([]string, 0) args = make([]string, 0)
} }
return &Request{path, opts, args} return &request{path, opts, args}
} }

View File

@ -48,21 +48,53 @@ var marshallers = map[EncodingType]Marshaller{
// Response is the result of a command request. Handlers write to the response, // Response is the result of a command request. Handlers write to the response,
// setting Error or Value. Response is returned to the client. // setting Error or Value. Response is returned to the client.
type Response struct { type Response interface {
req *Request Request() Request
Error *Error
Value interface{} // 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)
} }
// SetError updates the response Error. type response struct {
func (r *Response) SetError(err error, code ErrorType) { req Request
r.Error = &Error{Message: err.Error(), Code: code} err *Error
value interface{}
} }
// Marshal marshals out the response into a buffer. It uses the EncodingType func (r *response) Request() Request {
// on the Request to chose a Marshaller (Codec). return r.req
func (r *Response) Marshal() ([]byte, error) { }
if r.Error == nil && r.Value == nil {
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
}
func (r *response) SetError(err error, code ErrorType) {
r.err = &Error{Message: err.Error(), Code: code}
}
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") 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) return nil, fmt.Errorf("No marshaller found for encoding type '%s'", enc)
} }
if r.Error != nil { if r.err != nil {
return marshaller(r.Error) 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}
} }

View File

@ -13,10 +13,8 @@ type TestOutput struct {
func TestMarshalling(t *testing.T) { func TestMarshalling(t *testing.T) {
req := NewEmptyRequest() req := NewEmptyRequest()
res := Response{ res := NewResponse(req)
req: req, res.SetValue(TestOutput{"beep", "boop", 1337})
Value: TestOutput{"beep", "boop", 1337},
}
// get command global options so we can set the encoding option // get command global options so we can set the encoding option
cmd := Command{} cmd := Command{}
@ -31,7 +29,7 @@ func TestMarshalling(t *testing.T) {
} }
req.SetOption(EncShort, JSON) req.SetOption(EncShort, JSON)
req.convertOptions(options) req.ConvertOptions(options)
bytes, err := res.Marshal() bytes, err := res.Marshal()
if err != nil { if err != nil {