-
Notifications
You must be signed in to change notification settings - Fork 0
/
tree.go
129 lines (104 loc) · 3.19 KB
/
tree.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package router
import (
"fmt"
"strings"
)
// creates or finds a route based a path relative to a provided root-route.
// New routes will be created and appended to the tree if a wanted path does not exist.
// fullPath is the full path (for example "/aaaaa/bbbbb/")
// It returns the leaf route of provided path, and a bool indicating if the route was created
func createOrFindRoute(root *Route, fullPath string) (*Route, bool) {
// Create sub paths
subPaths := pathToSubPaths(fullPath)
if len(subPaths) == 0 {
return nil, false
}
// Check if appended route has a root-value "/"
if subPaths[0] == "/" {
return root, false
}
// Build route
curRoute := root
created := false
for _, path := range subPaths {
// Check if path is a parameter
if strings.HasPrefix(path, ":") {
// If the route already has a parameter subscribed, panic
if curRoute.paramRoute != nil {
panic(fmt.Sprintf("Path '%s' conflicts with existing route due to '%s'", fullPath, path))
}
paramRoute := newRoute(path)
curRoute.paramRoute = paramRoute
curRoute = paramRoute
continue
}
// Get subroute with matching path
subRoute, has := curRoute.subRoutes[path]
// If the subroute, create new route
if !has {
subRoute = newRoute(path)
curRoute.subRoutes[path] = subRoute
created = true
}
// Check if the subroute is static
if subRoute.static {
panic("Can not add new route on static route!")
}
curRoute = subRoute
}
return curRoute, created
}
// Search searches after a route relative to a provided root route with a given path.
// It returns "match", which indicates if a route was found.
// "r" is the route found. If match is true, then r is the matching route. Otherwise it is farthest route found based on the path.
func search(root *Route, path string) (match bool, r *Route, params map[string]string, fh *FileHandler) {
subPaths := pathToSubPaths(path)
params = make(map[string]string)
if len(subPaths) == 0 {
return false, nil, nil, nil
}
if root.static {
return false, root, nil, newFileHandler(root.staticPath, path)
}
// Check if provided path is referencing the root
if subPaths[0] == "/" {
return true, root, nil, nil
}
// Find matching route
curNode := root
for i, path := range subPaths {
// Check if any subroutes matches the path
if route, ok := curNode.subRoutes[path]; ok {
curNode = route
// Check if route is static
if curNode.static {
return false, curNode, params, newFileHandler(curNode.staticPath, strings.Join(subPaths[i+1:], "/"))
}
continue
}
// Otherwise check if a param-route exist
if curNode.paramRoute != nil {
curNode = curNode.paramRoute
params[strings.TrimLeft(curNode.value, ":")] = path // Add param value
continue
}
// No match found!
return false, curNode, params, nil
}
return true, curNode, params, nil
}
// ------ HELPERS ---------
func pathToSubPaths(path string) []string {
path = strings.TrimSpace(path)
// Check if path is root path
if path == "/" || path == "" {
return []string{"/"}
}
// Trim slashes on the edges
path = strings.Trim(path, "/")
subPaths := strings.Split(path, "/")
for i, path := range subPaths {
subPaths[i] = strings.ToLower(path)
}
return subPaths
}