diff --git a/httpr.go b/httpr.go index a67e232..d15edbe 100644 --- a/httpr.go +++ b/httpr.go @@ -41,83 +41,93 @@ type node struct { handler http.HandlerFunc } -func (n *node) get(path path, idx int) (http.HandlerFunc, Params) { - // Check if this node is a catch-all endpoint. - if n.endpoint[0] == '*' { - var p Params = Params{} - p[n.endpoint[1:]] = strings.Join(path[idx:], "/") - return n.handler, p - } +func (n *node) get(path path) (http.HandlerFunc, Params) { + pathLen := len(path) + curNode := n - // If this endpoint is a parameter, then add its name to a path's part. - // This will be used further to fill Params. - if n.endpoint[0] == ':' { - path[idx] = n.endpoint + ":" + path[idx] - } - - if len(path) == idx+1 { - var params Params = make(Params) - - for _, part := range path { - if part[0] == ':' { - param := strings.Split(part[1:], ":") - params[param[0]] = param[1] - } +outer: + for i := range path { + // Check if this node is a catch-all endpoint. + if curNode.endpoint[0] == '*' { + var p Params = Params{} + p[curNode.endpoint[1:]] = strings.Join(path[i:], "/") + return curNode.handler, p } - return n.handler, params - } - - if len(path) > idx+1 { - var wildcardOrParam *node - for _, next := range n.children { - if next.endpoint == path[idx+1] { - return next.get(path, idx+1) - } - - if next.endpoint[0] == ':' || next.endpoint[0] == '*' { - wildcardOrParam = next - } + // If this is a parametrised endpoint, then add its name to + // a path's part. It will be used further to parse parameters. + if curNode.endpoint[0] == ':' { + path[i] = curNode.endpoint + ":" + path[i] } - if wildcardOrParam != nil { - return wildcardOrParam.get(path, idx+1) + if pathLen == i+1 { + var params Params = make(Params) + + for _, part := range path { + if part[0] == ':' { + param := strings.Split(part[1:], ":") + params[param[0]] = param[1] + } + } + + return n.handler, params + } + + if pathLen > i+1 { + var paramNode *node + for _, next := range n.children { + if next.endpoint == path[i+1] { + curNode = next + continue outer + } + + if next.endpoint[0] == ':' || next.endpoint[0] == '*' { + paramNode = next + } + } + + if paramNode != nil { + curNode = paramNode + continue + } } } return nil, nil } -func (n *node) add(path path, idx int, handler http.HandlerFunc) error { - // If it is a last part of path, then set a handler to this node. - if len(path) == idx+1 { - n.endpoint = path[idx] - n.handler = handler - return nil - } +func (n *node) add(path path, handler http.HandlerFunc) error { + pathLen := len(path) + curNode := n - // Check if next part is a parameter and if it is, then look for - // an already existing endpoint with a different key. - if path[idx+1][0] == '*' || path[idx+1][0] == ':' { - for _, child := range n.children { - if (child.endpoint[0] == '*' || child.endpoint[0] == ':') && path[idx+1] != child.endpoint { - return errors.New("there is already a catch-all or regular param in there! You cannot add a second one") +outer: + for i := range path { + if pathLen == i+1 { + if curNode.handler != nil { + return errors.New("attempt to redefine a handler for already existing path") } - } - } - - // Check for an already existing endpoint. - for _, child := range n.children { - if child.endpoint == path[idx+1] { - child.add(path, idx+1, handler) + curNode.endpoint = path[i] + curNode.handler = handler return nil } - } - // No endpoint was found. - new_child := &node{endpoint: path[idx+1]} - new_child.add(path, idx+1, handler) - n.children = append(n.children, new_child) + for _, child := range n.children { + firstChar := path[i+1][0] + if (firstChar == ':' || firstChar == '*') && firstChar == child.endpoint[0] { + curNode = child + continue outer + } + + if child.endpoint == path[i+1] { + curNode = child + continue outer + } + } + + newChild := &node{endpoint: path[i+1]} + curNode.children = append(curNode.children, newChild) + curNode = newChild + } return nil }