package mooh import ( "errors" "fmt" "net/http" "strings" ) type Dispatcher struct { Routes []Route RouteAccess map[string]*Route } func BuildDispatcher() Dispatcher { router := Dispatcher{} router.RouteAccess = map[string]*Route{} return router } func (self *Dispatcher) GetRoute(path string) *Route { r := self.RouteAccess[path] if r != nil { return r } return nil } func (self *Dispatcher) AddRoute(path string, method string, code func(*Request) (Response, error)) { r := self.GetRoute(path) if r == nil { route := MakeRoute(path, method, code) self.Routes = append(self.Routes, route) self.RouteAccess[path] = &route } else { r.Executors[method] = code r.Methods[method] = true } } 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() { // }