// Copyright 2018-2025 Martin Riedl // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package httprouter import ( "strings" ) type route struct { name string placeholderName string isWildcard bool handler Handler routes map[string]*route } const ( parameterPrefix = ":" wildcard = "*" ) func newRoute() *route { return &route{routes: make(map[string]*route)} } func (r *route) parsePath(path string, create bool) (*route, bool, map[string]string) { parts := strings.SplitN(path, "/", 2) // parse parameters var parameters map[string]string if !create && r.name != parts[0] { parameters = make(map[string]string) parameters[r.placeholderName] = parts[0] } // check for wildcard match if !create && r.isWildcard { parameters = make(map[string]string) parameters[wildcard] = path return r, false, parameters } // more path is available if len(parts) == 2 { // parse next part element subParts := strings.SplitN(parts[1], "/", 2) subName := subParts[0] // find sub route element var subRoute *route var ok bool if create && isParameter(subName) { subRoute, ok = r.routes[parameterPrefix] } else { subRoute, ok = r.routes[subName] } // route not found, try to use placeholder path if !create && !ok { subRoute, ok = r.routes[parameterPrefix] } // route still not found, try to use wildcard path if !create && !ok { subRoute, ok = r.routes[wildcard] } // check for creation subRouteCreated := false if !ok && !create { // no route found return nil, false, nil } else if !ok { subRoute = newRoute() subRoute.parseName(subName) r.routes[subRoute.name] = subRoute subRouteCreated = true } // parse sub-route matchingRoute, created, subParameters := subRoute.parsePath(parts[1], create) return matchingRoute, subRouteCreated || created, mergeParameterMaps(parameters, subParameters) } // last element in path return r, false, parameters } func (r *route) parseName(name string) { if isParameter(name) { r.name = parameterPrefix r.placeholderName = name[1:] } else if isWildcard(name) { r.name = wildcard r.isWildcard = true } else { r.name = name } } func mergeParameterMaps(m1, m2 map[string]string) map[string]string { if m1 == nil { return m2 } else if m2 == nil { return m1 } // merge both maps for key, val := range m2 { m1[key] = val } return m1 } func isParameter(s string) bool { return strings.Index(s, parameterPrefix) == 0 && len(s) > 1 } func isWildcard(s string) bool { return s == wildcard }