@cryptotaxi247 / kubo / commits / dd2a10509

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 +}