commands: Implemented Command
Matt Bell committed
Oct 8, 2014 at 14:27 UTC
dd2a10509f3dc548d13dacfe9c0f93a81f507ffc
1 file changed
+85
commands/command.go
new
+85
@@ -0,0 +1,85 @@
1
+package commands
2
+
3
+import (
4
+ "fmt"
5
+ "strings"
6
+ "reflect"
7
+)
8
+
9
+type Command struct {
10
+ Help string
11
+ Options []Option
12
+ f func(*Request) (interface{}, error)
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
+ // TODO: check for duplicate option names
23
+
24
+ if _, ok := c.subcommands[id]; ok {
25
+ return fmt.Errorf("There is already a subcommand registered with id '%s'", id)
26
+ }
27
+
28
+ c.subcommands[id] = sub
29
+ return nil
30
+}
31
+
32
+// Call invokes the command at the given subcommand path
33
+func (c *Command) Call(path []string, req *Request) (interface{}, error) {
34
+ options := make([]Option, len(c.Options))
35
+ copy(options, c.Options)
36
+ cmd := c
37
+
38
+ if path != nil {
39
+ for i, id := range path {
40
+ cmd = c.Sub(id)
41
+
42
+ if cmd == nil {
43
+ pathS := strings.Join(path[0:i], "/")
44
+ return nil, fmt.Errorf("Undefined command: '%s'", pathS)
45
+ }
46
+
47
+ options = append(options, cmd.Options...)
48
+ }
49
+ }
50
+
51
+ optionsMap := make(map[string]Option)
52
+ for _, opt := range options {
53
+ for _, name := range opt.Names {
54
+ optionsMap[name] = opt
55
+ }
56
+ }
57
+
58
+ for k, v := range req.options {
59
+ opt, ok := optionsMap[k]
60
+
61
+ if !ok {
62
+ return nil, fmt.Errorf("Unrecognized command option: '%s'", k)
63
+ }
64
+
65
+ for _, name := range opt.Names {
66
+ if _, ok = req.options[name]; name != k && ok {
67
+ return nil, fmt.Errorf("Duplicate command options were provided ('%s' and '%s')",
68
+ k, name)
69
+ }
70
+ }
71
+
72
+ kind := reflect.TypeOf(v).Kind()
73
+ if kind != opt.Type {
74
+ return nil, fmt.Errorf("Option '%s' should be type '%s', but got type '%s'",
75
+ k, opt.Type.String(), kind.String())
76
+ }
77
+ }
78
+
79
+ return cmd.f(req)
80
+}
81
+
82
+// Sub returns the subcommand with the given id
83
+func (c *Command) Sub(id string) *Command {
84
+ return c.subcommands[id]
85
+}