package mooh import ( "errors" "fmt" "net/http" "strings" ) type Router struct { routes []*Route knownPaths map[string]map[string]bool } func BuildRouter() Router { router := Router{} router.knownPaths = map[string]map[string]bool{} return router } func (self *Router) 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 *Router) AddRoute(route *Route) error { if self.routeIsKnown(route) == true { return errors.New(fmt.Sprintf("The route %s with the method %s already exist.", route.Path, route.Method)) } route.init() self.routes = append(self.routes, route) self.knownPaths[route.Path][route.Method] = true return nil } func (self *Router) 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 *Router) 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 (self *Router) GetRouteList() []string { routes := make([]string, len(self.knownPaths)) i := 0 for path, _ := range self.knownPaths { routes[i] = path i = i + 1 } return routes } func (self *Router) HasPath(path string) bool { if self.knownPaths[path] != nil { return true } return false } func (self *Router) RemovePath(path string) error { p := self.HasPath(path) if p == false { return errors.New("foo") } delete(self.knownPaths, path) newRoutes := []*Route{} for _, p := range self.routes { if p.Path != path { newRoutes = append(newRoutes, p) } } self.routes = newRoutes return nil } func (self *Router) GetAllRoutes() []*Route { return self.routes } func (self *Router) GetAllRoutesByMethods (method string) []*Route { routes := []*Route{} for _, r := range self.routes { if r.Method == method { routes = append(routes, r) } } return routes } func (self *Router) GetMethodsForPath(path string) []string { p := self.knownPaths[path] m := make([]string, len(p)) i := 0 for k, _ := range p { m[i] = k i = i + 1 } return m }