about summary refs log tree commit diff
path: root/router.go
diff options
context:
space:
mode:
Diffstat (limited to 'router.go')
-rw-r--r--router.go99
1 files changed, 99 insertions, 0 deletions
diff --git a/router.go b/router.go
new file mode 100644
index 0000000..e5c8e13
--- /dev/null
+++ b/router.go
@@ -0,0 +1,99 @@
+package mooh
+
+import (
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+)
+
+type Dispatcher struct {
+	routes     []*Route
+	knownPaths map[string]map[string]bool
+}
+
+func BuildDispatcher() Dispatcher {
+	router := Dispatcher{}
+	router.knownPaths = map[string]map[string]bool{}
+	return router
+}
+
+func (self *Dispatcher) routeIsKnown(route *Route) bool {
+	if self.knownPaths[route.Path] == nil {
+		self.knownPaths[route.Path] = map[string]bool{}
+		return false
+	} else if self.knownPaths[route.Path][route.Method] == false {
+		return false
+	}
+	return true
+}
+
+func (self *Dispatcher) AddRoute(route *Route) error {
+	if self.routeIsKnown(route) == true {
+		return errors.New(fmt.Sprintf("Can't add twice the same route. The route %s with the method %s is already added", route.Path, route.Method))
+	}
+
+	route.init()
+	self.routes = append(self.routes, route)
+	self.knownPaths[route.Path][route.Method] = true
+	return nil
+}
+
+func (self *Dispatcher) Match(request *http.Request) (*Match, error) {
+
+	matches := []*Match{}
+
+	method := request.Method
+	components := strings.Split(request.URL.Path, "/")
+
+	for _, r := range self.routes {
+		match := r.Match(method, components)
+		if match != nil {
+			matches = append(matches, match)
+		}
+	}
+
+	if len(matches) == 0 {
+		return nil, nil
+	} else if len(matches) == 1 {
+		return matches[0], nil
+	} else {
+		return self.disambiguateMatches(request.URL.Path, matches)
+	}
+	return nil, nil
+}
+
+func (self *Dispatcher) disambiguateMatches(path string, matches []*Match) (*Match, error) {
+	min := -1
+	found := []*Match{}
+
+	for _, m := range matches {
+		req := m.Route.requiredNamedComponents
+		vars := len(req)
+		if min == -1 || vars < min {
+			found = append(found, m)
+			min = vars
+		} else if vars == min {
+			found = append(found, m)
+		}
+	}
+
+	if len(found) > 1 {
+		msg := fmt.Sprintf("Ambiguous match: path %s could match any of:", path)
+		for _, f := range found {
+			msg = fmt.Sprintf("%s %s", msg, f.Route.Path)
+		}
+		err := errors.New(msg)
+		return nil, err
+	}
+	return found[0], nil
+}
+
+// func (*dispatcher) ListRoutes() {
+// }
+
+// func (*dispatcher) AddRoutes() {
+// }
+
+// func (self *dispatcher) UriFor() {
+// }