diff options
Diffstat (limited to 'router.go')
-rw-r--r-- | router.go | 99 |
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() { +// } |