@cryptotaxi247 / kubo / commits / 43670971d

commands: Formatted code

Matt Bell committed Oct 10, 2014 at 14:24 UTC 43670971d802d7d381c87aeb61b22ff84fd5453f
6 files changed +314 -310
commands/command.go
+98 -98
@@ -1,120 +1,120 @@
1 package commands
2
3 import (
4 - "fmt"
5 - "strings"
6 - "reflect"
4 + "fmt"
5 + "reflect"
6 + "strings"
7 )
8
9 type Command struct {
10 - Help string
11 - Options []Option
12 - f func(*Request, *Response)
13 - subcommands map[string]*Command
10 + Help string
11 + Options []Option
12 + f func(*Request, *Response)
13 + subcommands map[string]*Command
14 }
15
16 // Register adds a subcommand
17 func (c *Command) Register(id string, sub *Command) error {
18 - if c.subcommands == nil {
19 - c.subcommands = make(map[string]*Command)
20 - }
21 -
22 - // check for duplicate option names (only checks downwards)
23 - names := make(map[string]bool)
24 - globalCommand.checkOptions(names)
25 - c.checkOptions(names)
26 - err := sub.checkOptions(names)
27 - if err != nil {
28 - return err
29 - }
30 -
31 - if _, ok := c.subcommands[id]; ok {
32 - return fmt.Errorf("There is already a subcommand registered with id '%s'", id)
33 - }
34 -
35 - c.subcommands[id] = sub
36 - return nil
18 + if c.subcommands == nil {
19 + c.subcommands = make(map[string]*Command)
20 + }
21 +
22 + // check for duplicate option names (only checks downwards)
23 + names := make(map[string]bool)
24 + globalCommand.checkOptions(names)
25 + c.checkOptions(names)
26 + err := sub.checkOptions(names)
27 + if err != nil {
28 + return err
29 + }
30 +
31 + if _, ok := c.subcommands[id]; ok {
32 + return fmt.Errorf("There is already a subcommand registered with id '%s'", id)
33 + }
34 +
35 + c.subcommands[id] = sub
36 + return nil
37 }
38
39 // Call invokes the command at the given subcommand path
40 func (c *Command) Call(path []string, req *Request) *Response {
41 - options := make([]Option, len(c.Options))
42 - copy(options, c.Options)
43 - options = append(options, globalOptions...)
44 - cmd := c
45 - res := &Response{ req: req }
46 -
47 - if path != nil {
48 - for i, id := range path {
49 - cmd = c.Sub(id)
50 -
51 - if cmd == nil {
52 - pathS := strings.Join(path[0:i], "/")
53 - res.SetError(fmt.Errorf("Undefined command: '%s'", pathS), Client)
54 - return res
55 - }
56 -
57 - options = append(options, cmd.Options...)
58 - }
59 - }
60 -
61 - optionsMap := make(map[string]Option)
62 - for _, opt := range options {
63 - for _, name := range opt.Names {
64 - optionsMap[name] = opt
65 - }
66 - }
67 -
68 - for k, v := range req.options {
69 - opt, ok := optionsMap[k]
70 -
71 - if !ok {
72 - res.SetError(fmt.Errorf("Unrecognized command option: '%s'", k), Client)
73 - return res
74 - }
75 -
76 - for _, name := range opt.Names {
77 - if _, ok = req.options[name]; name != k && ok {
78 - res.SetError(fmt.Errorf("Duplicate command options were provided ('%s' and '%s')",
79 - k, name), Client)
80 - return res
81 - }
82 - }
83 -
84 - kind := reflect.TypeOf(v).Kind()
85 - if kind != opt.Type {
86 - res.SetError(fmt.Errorf("Option '%s' should be type '%s', but got type '%s'",
87 - k, opt.Type.String(), kind.String()), Client)
88 - return res
89 - }
90 - }
91 -
92 - cmd.f(req, res)
93 -
94 - return res
41 + options := make([]Option, len(c.Options))
42 + copy(options, c.Options)
43 + options = append(options, globalOptions...)
44 + cmd := c
45 + res := &Response{req: req}
46 +
47 + if path != nil {
48 + for i, id := range path {
49 + cmd = c.Sub(id)
50 +
51 + if cmd == nil {
52 + pathS := strings.Join(path[0:i], "/")
53 + res.SetError(fmt.Errorf("Undefined command: '%s'", pathS), Client)
54 + return res
55 + }
56 +
57 + options = append(options, cmd.Options...)
58 + }
59 + }
60 +
61 + optionsMap := make(map[string]Option)
62 + for _, opt := range options {
63 + for _, name := range opt.Names {
64 + optionsMap[name] = opt
65 + }
66 + }
67 +
68 + for k, v := range req.options {
69 + opt, ok := optionsMap[k]
70 +
71 + if !ok {
72 + res.SetError(fmt.Errorf("Unrecognized command option: '%s'", k), Client)
73 + return res
74 + }
75 +
76 + for _, name := range opt.Names {
77 + if _, ok = req.options[name]; name != k && ok {
78 + res.SetError(fmt.Errorf("Duplicate command options were provided ('%s' and '%s')",
79 + k, name), Client)
80 + return res
81 + }
82 + }
83 +
84 + kind := reflect.TypeOf(v).Kind()
85 + if kind != opt.Type {
86 + res.SetError(fmt.Errorf("Option '%s' should be type '%s', but got type '%s'",
87 + k, opt.Type.String(), kind.String()), Client)
88 + return res
89 + }
90 + }
91 +
92 + cmd.f(req, res)
93 +
94 + return res
95 }
96
97 // Sub returns the subcommand with the given id
98 func (c *Command) Sub(id string) *Command {
99 - return c.subcommands[id]
99 + return c.subcommands[id]
100 }
101
102 func (c *Command) checkOptions(names map[string]bool) error {
103 - for _, opt := range c.Options {
104 - for _, name := range opt.Names {
105 - if _, ok := names[name]; ok {
106 - return fmt.Errorf("Multiple options are using the same name ('%s')", name)
107 - }
108 - names[name] = true
109 - }
110 - }
111 -
112 - for _, cmd := range c.subcommands {
113 - err := cmd.checkOptions(names)
114 - if err != nil {
115 - return err
116 - }
117 - }
118 -
119 - return nil
103 + for _, opt := range c.Options {
104 + for _, name := range opt.Names {
105 + if _, ok := names[name]; ok {
106 + return fmt.Errorf("Multiple options are using the same name ('%s')", name)
107 + }
108 + names[name] = true
109 + }
110 + }
111 +
112 + for _, cmd := range c.subcommands {
113 + err := cmd.checkOptions(names)
114 + if err != nil {
115 + return err
116 + }
117 + }
118 +
119 + return nil
120 }
commands/command_test.go
+115 -115
@@ -3,122 +3,122 @@ package commands
3 import "testing"
4
5 func TestOptionValidation(t *testing.T) {
6 - cmd := Command{
7 - Options: []Option{
8 - Option{ []string{ "b", "beep" }, Int },
9 - Option{ []string{ "B", "boop" }, String },
10 - },
11 - f: func(req *Request, res *Response) {},
12 - }
13 -
14 - req := NewRequest()
15 - req.options["foo"] = 5
16 - res := cmd.Call(nil, req)
17 - if res.Error == nil {
18 - t.Error("Should have failed (unrecognized command)")
19 - }
20 -
21 - req = NewRequest()
22 - req.options["beep"] = 5
23 - req.options["b"] = 10
24 - res = cmd.Call(nil, req)
25 - if res.Error == nil {
26 - t.Error("Should have failed (duplicate options)")
27 - }
28 -
29 - req = NewRequest()
30 - req.options["beep"] = "foo"
31 - res = cmd.Call(nil, req)
32 - if res.Error == nil {
33 - t.Error("Should have failed (incorrect type)")
34 - }
35 -
36 - req = NewRequest()
37 - req.options["beep"] = 5
38 - res = cmd.Call(nil, req)
39 - if res.Error != nil {
40 - t.Error("Should have passed")
41 - }
42 -
43 - req = NewRequest()
44 - req.options["beep"] = 5
45 - req.options["boop"] = "test"
46 - res = cmd.Call(nil, req)
47 - if res.Error != nil {
48 - t.Error("Should have passed")
49 - }
50 -
51 - req = NewRequest()
52 - req.options["b"] = 5
53 - req.options["B"] = "test"
54 - res = cmd.Call(nil, req)
55 - if res.Error != nil {
56 - t.Error("Should have passed")
57 - }
58 -
59 - req = NewRequest()
60 - req.options["enc"] = "json"
61 - res = cmd.Call(nil, req)
62 - if res.Error != nil {
63 - t.Error("Should have passed")
64 - }
6 + cmd := Command{
7 + Options: []Option{
8 + Option{[]string{"b", "beep"}, Int},
9 + Option{[]string{"B", "boop"}, String},
10 + },
11 + f: func(req *Request, res *Response) {},
12 + }
13 +
14 + req := NewRequest()
15 + req.options["foo"] = 5
16 + res := cmd.Call(nil, req)
17 + if res.Error == nil {
18 + t.Error("Should have failed (unrecognized command)")
19 + }
20 +
21 + req = NewRequest()
22 + req.options["beep"] = 5
23 + req.options["b"] = 10
24 + res = cmd.Call(nil, req)
25 + if res.Error == nil {
26 + t.Error("Should have failed (duplicate options)")
27 + }
28 +
29 + req = NewRequest()
30 + req.options["beep"] = "foo"
31 + res = cmd.Call(nil, req)
32 + if res.Error == nil {
33 + t.Error("Should have failed (incorrect type)")
34 + }
35 +
36 + req = NewRequest()
37 + req.options["beep"] = 5
38 + res = cmd.Call(nil, req)
39 + if res.Error != nil {
40 + t.Error("Should have passed")
41 + }
42 +
43 + req = NewRequest()
44 + req.options["beep"] = 5
45 + req.options["boop"] = "test"
46 + res = cmd.Call(nil, req)
47 + if res.Error != nil {
48 + t.Error("Should have passed")
49 + }
50 +
51 + req = NewRequest()
52 + req.options["b"] = 5
53 + req.options["B"] = "test"
54 + res = cmd.Call(nil, req)
55 + if res.Error != nil {
56 + t.Error("Should have passed")
57 + }
58 +
59 + req = NewRequest()
60 + req.options["enc"] = "json"
61 + res = cmd.Call(nil, req)
62 + if res.Error != nil {
63 + t.Error("Should have passed")
64 + }
65 }
66
67 func TestRegistration(t *testing.T) {
68 - cmds := []*Command{
69 - &Command{
70 - Options: []Option{
71 - Option{ []string{ "beep" }, Int },
72 - },
73 - f: func(req *Request, res *Response) {},
74 - },
75 -
76 - &Command{
77 - Options: []Option{
78 - Option{ []string{ "boop" }, Int },
79 - },
80 - f: func(req *Request, res *Response) {},
81 - },
82 -
83 - &Command{
84 - Options: []Option{
85 - Option{ []string{ "boop" }, String },
86 - },
87 - f: func(req *Request, res *Response) {},
88 - },
89 -
90 - &Command{
91 - Options: []Option{
92 - Option{ []string{ "bop" }, String },
93 - },
94 - f: func(req *Request, res *Response) {},
95 - },
96 -
97 - &Command{
98 - Options: []Option{
99 - Option{ []string{ "enc" }, String },
100 - },
101 - f: func(req *Request, res *Response) {},
102 - },
103 - }
104 -
105 - err := cmds[0].Register("foo", cmds[1])
106 - if err != nil {
107 - t.Error("Should have passed")
108 - }
109 -
110 - err = cmds[0].Register("bar", cmds[2])
111 - if err == nil {
112 - t.Error("Should have failed (option name collision)")
113 - }
114 -
115 - err = cmds[0].Register("foo", cmds[3])
116 - if err == nil {
117 - t.Error("Should have failed (subcommand name collision)")
118 - }
119 -
120 - err = cmds[0].Register("baz", cmds[4])
121 - if err == nil {
122 - t.Error("Should have failed (option name collision with global options)")
123 - }
68 + cmds := []*Command{
69 + &Command{
70 + Options: []Option{
71 + Option{[]string{"beep"}, Int},
72 + },
73 + f: func(req *Request, res *Response) {},
74 + },
75 +
76 + &Command{
77 + Options: []Option{
78 + Option{[]string{"boop"}, Int},
79 + },
80 + f: func(req *Request, res *Response) {},
81 + },
82 +
83 + &Command{
84 + Options: []Option{
85 + Option{[]string{"boop"}, String},
86 + },
87 + f: func(req *Request, res *Response) {},
88 + },
89 +
90 + &Command{
91 + Options: []Option{
92 + Option{[]string{"bop"}, String},
93 + },
94 + f: func(req *Request, res *Response) {},
95 + },
96 +
97 + &Command{
98 + Options: []Option{
99 + Option{[]string{"enc"}, String},
100 + },
101 + f: func(req *Request, res *Response) {},
102 + },
103 + }
104 +
105 + err := cmds[0].Register("foo", cmds[1])
106 + if err != nil {
107 + t.Error("Should have passed")
108 + }
109 +
110 + err = cmds[0].Register("bar", cmds[2])
111 + if err == nil {
112 + t.Error("Should have failed (option name collision)")
113 + }
114 +
115 + err = cmds[0].Register("foo", cmds[3])
116 + if err == nil {
117 + t.Error("Should have failed (subcommand name collision)")
118 + }
119 +
120 + err = cmds[0].Register("baz", cmds[4])
121 + if err == nil {
122 + t.Error("Should have failed (option name collision with global options)")
123 + }
124 }
commands/option.go
+14 -13
@@ -3,29 +3,30 @@ package commands
3 import "reflect"
4
5 const (
6 - Invalid = reflect.Invalid
7 - Bool = reflect.Bool
8 - Int = reflect.Int
9 - Uint = reflect.Uint
10 - Float = reflect.Float32
11 - String = reflect.String
6 + Invalid = reflect.Invalid
7 + Bool = reflect.Bool
8 + Int = reflect.Int
9 + Uint = reflect.Uint
10 + Float = reflect.Float32
11 + String = reflect.String
12 )
13
14 // Option is used to specify a field that will be provided by a consumer
15 type Option struct {
16 - Names []string // a list of unique names to
17 - Type reflect.Kind // value must be this type
16 + Names []string // a list of unique names to
17 + Type reflect.Kind // value must be this type
18
19 - // TODO: add more features(?):
20 - //Default interface{} // the default value (ignored if `Required` is true)
21 - //Required bool // whether or not the option must be provided
19 + // TODO: add more features(?):
20 + //Default interface{} // the default value (ignored if `Required` is true)
21 + //Required bool // whether or not the option must be provided
22 }
23
24 // options that are used by this package
25 var globalOptions []Option = []Option{
26 - Option{ []string{ "enc", "encoding" }, String },
26 + Option{[]string{"enc", "encoding"}, String},
27 }
28 +
29 // the above array of Options, wrapped in a Command
30 var globalCommand *Command = &Command{
30 - Options: globalOptions,
31 + Options: globalOptions,
32 }
commands/request.go
+13 -13
@@ -2,29 +2,29 @@ package commands
2
3 // Request represents a call to a command from a consumer
4 type Request struct {
5 - options map[string]interface{}
6 - arguments []string
5 + options map[string]interface{}
6 + arguments []string
7 }
8
9 func (r *Request) Option(name string) interface{} {
10 - return r.options[name]
10 + return r.options[name]
11 }
12
13 func (r *Request) SetOption(option Option, value interface{}) {
14 - // saves the option value in the map, indexed by each name
15 - // (so commands can retrieve it using any of the names)
16 - for _, name := range option.Names {
17 - r.options[name] = value
18 - }
14 + // saves the option value in the map, indexed by each name
15 + // (so commands can retrieve it using any of the names)
16 + for _, name := range option.Names {
17 + r.options[name] = value
18 + }
19 }
20
21 func (r *Request) Arguments() []string {
22 - return r.arguments
22 + return r.arguments
23 }
24
25 func NewRequest() *Request {
26 - return &Request{
27 - make(map[string]interface{}),
28 - make([]string, 0),
29 - }
26 + return &Request{
27 + make(map[string]interface{}),
28 + make([]string, 0),
29 + }
30 }
commands/response.go
+43 -40
@@ -1,74 +1,77 @@
1 package commands
2
3 import (
4 - "fmt"
5 - "strings"
6 - "encoding/json"
7 - "encoding/xml"
4 + "encoding/json"
5 + "encoding/xml"
6 + "fmt"
7 + "strings"
8 )
9
10 type ErrorType uint
11 +
12 const (
12 - Normal ErrorType = iota // general errors
13 - Client // error was caused by the client, (e.g. invalid CLI usage)
14 - // TODO: add more types of errors for better error-specific handling
13 + Normal ErrorType = iota // general errors
14 + Client // error was caused by the client, (e.g. invalid CLI usage)
15 + // TODO: add more types of errors for better error-specific handling
16 )
17
18 // Error is a struct for marshalling errors
19 type Error struct {
19 - Message string
20 - Code ErrorType
20 + Message string
21 + Code ErrorType
22 }
23
24 type EncodingType string
25 +
26 const (
25 - Json = "json"
26 - Xml = "xml"
27 - // TODO: support more encoding types
27 + Json = "json"
28 + Xml = "xml"
29 + // TODO: support more encoding types
30 )
31
30 -type Marshaller func(v interface{})([]byte, error)
32 +type Marshaller func(v interface{}) ([]byte, error)
33 +
34 var marshallers = map[EncodingType]Marshaller{
32 - Json: json.Marshal,
33 - Xml: xml.Marshal,
35 + Json: json.Marshal,
36 + Xml: xml.Marshal,
37 }
38
39 type Response struct {
37 - req *Request
38 - Error error
39 - ErrorType ErrorType
40 - Value interface{}
40 + req *Request
41 + Error error
42 + ErrorType ErrorType
43 + Value interface{}
44 }
45
46 func (r *Response) SetError(err error, errType ErrorType) {
44 - r.Error = err
45 - r.ErrorType = errType
47 + r.Error = err
48 + r.ErrorType = errType
49 }
50
51 func (r *Response) FormatError() Error {
49 - return Error{ r.Error.Error(), r.ErrorType }
52 + return Error{r.Error.Error(), r.ErrorType}
53 }
54
55 func (r *Response) Marshal() ([]byte, error) {
53 - if r.Error == nil && r.Value == nil {
54 - return nil, fmt.Errorf("No error or value set, there is nothing to marshal")
55 - }
56 + if r.Error == nil && r.Value == nil {
57 + return nil, fmt.Errorf("No error or value set, there is nothing to marshal")
58 + }
59
57 - enc := r.req.Option("enc")
58 - if enc == nil {
59 - return nil, fmt.Errorf("No encoding type was specified")
60 - }
61 - encType := EncodingType(strings.ToLower(enc.(string)))
60 + enc := r.req.Option("enc")
61 + if enc == nil {
62 + return nil, fmt.Errorf("No encoding type was specified")
63 + }
64 + encType := EncodingType(strings.ToLower(enc.(string)))
65
63 - marshaller, ok := marshallers[encType]
64 - if !ok {
65 - return nil, fmt.Errorf("No marshaller found for encoding type '%s'", enc)
66 - }
66 + marshaller, ok := marshallers[encType]
67 + if !ok {
68 + return nil, fmt.Errorf("No marshaller found for encoding type '%s'", enc)
69 + }
70
68 - if r.Error != nil {
69 - err := r.FormatError()
70 - return marshaller(err)
71 - } else {
72 - return marshaller(r.Value)
73 - }
71 + if r.Error != nil {
72 + err := r.FormatError()
73 + return marshaller(err)
74 + } else {
75 + return marshaller(r.Value)
76 + }
77 }
commands/response_test.go
+31 -31
@@ -1,45 +1,45 @@
1 package commands
2
3 import (
4 - "testing"
5 - "fmt"
4 + "fmt"
5 + "testing"
6 )
7
8 type TestOutput struct {
9 - Foo, Bar string
10 - Baz int
9 + Foo, Bar string
10 + Baz int
11 }
12
13 func TestMarshalling(t *testing.T) {
14 - req := NewRequest()
14 + req := NewRequest()
15
16 - res := Response{
17 - req: req,
18 - Value: TestOutput{ "beep", "boop", 1337 },
19 - }
16 + res := Response{
17 + req: req,
18 + Value: TestOutput{"beep", "boop", 1337},
19 + }
20
21 - _, err := res.Marshal()
22 - if err == nil {
23 - t.Error("Should have failed (no encoding type specified in request)")
24 - }
21 + _, err := res.Marshal()
22 + if err == nil {
23 + t.Error("Should have failed (no encoding type specified in request)")
24 + }
25
26 - req.SetOption(globalOptions[0], Json)
27 - bytes, err := res.Marshal()
28 - if err != nil {
29 - t.Error("Should have passed")
30 - }
31 - output := string(bytes)
32 - if output != "{\"Foo\":\"beep\",\"Bar\":\"boop\",\"Baz\":1337}" {
33 - t.Error("Incorrect JSON output")
34 - }
26 + req.SetOption(globalOptions[0], Json)
27 + bytes, err := res.Marshal()
28 + if err != nil {
29 + t.Error("Should have passed")
30 + }
31 + output := string(bytes)
32 + if output != "{\"Foo\":\"beep\",\"Bar\":\"boop\",\"Baz\":1337}" {
33 + t.Error("Incorrect JSON output")
34 + }
35
36 - res.SetError(fmt.Errorf("You broke something!"), Client)
37 - bytes, err = res.Marshal()
38 - if err != nil {
39 - t.Error("Should have passed")
40 - }
41 - output = string(bytes)
42 - if output != "{\"Message\":\"You broke something!\",\"Code\":1}" {
43 - t.Error("Incorrect JSON output")
44 - }
36 + res.SetError(fmt.Errorf("You broke something!"), Client)
37 + bytes, err = res.Marshal()
38 + if err != nil {
39 + t.Error("Should have passed")
40 + }
41 + output = string(bytes)
42 + if output != "{\"Message\":\"You broke something!\",\"Code\":1}" {
43 + t.Error("Incorrect JSON output")
44 + }
45 }