httprouter/route.go
2019-08-03 18:52:58 +02:00

110 lines
2.6 KiB
Go
Executable file

// Copyright 2018-2019 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 gohttprouter
import (
"strings"
)
type route struct {
name string
placeholderName string
handler Handler
routes map[string]*route
}
const parameterPrefix = ":"
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]
}
// 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]
}
// check for creation
subRouteCreated := false
if !ok && !create {
// no route found
return nil, false, nil
} else if !ok && create {
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 {
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
}