From dd2a10509f3dc548d13dacfe9c0f93a81f507ffc Mon Sep 17 00:00:00 2001
From: Matt Bell <mappum@gmail.com>
Date: Wed, 8 Oct 2014 14:27:36 -0700
Subject: [PATCH] commands: Implemented Command

---
 commands/command.go | 85 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 85 insertions(+)
 create mode 100644 commands/command.go

diff --git a/commands/command.go b/commands/command.go
new file mode 100644
index 000000000..d118d780e
--- /dev/null
+++ b/commands/command.go
@@ -0,0 +1,85 @@
+package commands
+
+import (
+  "fmt"
+  "strings"
+  "reflect"
+)
+
+type Command struct {
+  Help string
+  Options []Option
+  f func(*Request) (interface{}, error)
+  subcommands map[string]*Command
+}
+
+// Register adds a subcommand
+func (c *Command) Register(id string, sub *Command) error {
+  if c.subcommands == nil {
+    c.subcommands = make(map[string]*Command)
+  }
+
+  // TODO: check for duplicate option names
+
+  if _, ok := c.subcommands[id]; ok {
+    return fmt.Errorf("There is already a subcommand registered with id '%s'", id)
+  }
+
+  c.subcommands[id] = sub
+  return nil
+}
+
+// Call invokes the command at the given subcommand path
+func (c *Command) Call(path []string, req *Request) (interface{}, error) {
+  options := make([]Option, len(c.Options))
+  copy(options, c.Options)
+  cmd := c
+
+  if path != nil {
+    for i, id := range path {
+      cmd = c.Sub(id)
+
+      if cmd == nil {
+        pathS := strings.Join(path[0:i], "/")
+        return nil, fmt.Errorf("Undefined command: '%s'", pathS)
+      }
+
+      options = append(options, cmd.Options...)
+    }
+  }
+
+  optionsMap := make(map[string]Option)
+  for _, opt := range options {
+    for _, name := range opt.Names {
+      optionsMap[name] = opt
+    }
+  }
+
+  for k, v := range req.options {
+    opt, ok := optionsMap[k]
+
+    if !ok {
+      return nil, fmt.Errorf("Unrecognized command option: '%s'", k)
+    }
+
+    for _, name := range opt.Names {
+      if _, ok = req.options[name]; name != k && ok {
+        return nil, fmt.Errorf("Duplicate command options were provided ('%s' and '%s')",
+          k, name)
+      }
+    }
+
+    kind := reflect.TypeOf(v).Kind()
+    if kind != opt.Type {
+      return nil, fmt.Errorf("Option '%s' should be type '%s', but got type '%s'",
+        k, opt.Type.String(), kind.String())
+    }
+  }
+
+  return cmd.f(req)
+}
+
+// Sub returns the subcommand with the given id
+func (c *Command) Sub(id string) *Command {
+  return c.subcommands[id]
+}
-- 
GitLab