-
Notifications
You must be signed in to change notification settings - Fork 30
/
Copy pathrouter.go
157 lines (136 loc) · 3.47 KB
/
router.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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package golf
import (
"fmt"
)
// HandlerFunc is the type of the handler function that Golf accepts.
type HandlerFunc func(ctx *Context)
// ErrorHandlerFunc is the type of the function that handles error in Golf.
type ErrorHandlerFunc func(ctx *Context, data ...map[string]interface{})
type router struct {
trees map[string]*node
}
func newRouter() *router {
return &router{trees: make(map[string]*node)}
}
func splitURLPath(path string) (parts []string, names map[string]int) {
var (
nameidx = -1
partidx int
paramCounter int
)
for i := 0; i < len(path); i++ {
if names == nil {
names = make(map[string]int)
}
// recording name
if nameidx != -1 {
//found /
if path[i] == '/' {
names[path[nameidx:i]] = paramCounter
paramCounter++
nameidx = -1 // switch to normal recording
partidx = i
}
} else {
if path[i] == ':' || path[i] == '*' {
if path[i-1] != '/' {
panic(fmt.Errorf("Invalid parameter : or * should always be after / - %q", path))
}
nameidx = i + 1
if partidx != i {
parts = append(parts, path[partidx:i])
}
parts = append(parts, path[i:nameidx])
}
}
}
if nameidx != -1 {
names[path[nameidx:]] = paramCounter
paramCounter++
} else if partidx < len(path) {
parts = append(parts, path[partidx:])
}
return
}
func (router *router) FindRoute(method string, path string) (HandlerFunc, Parameter, error) {
node := router.trees[method]
if node == nil {
return nil, Parameter{}, fmt.Errorf("Can not find route")
}
matchedNode, err := node.findRoute(path)
if err != nil {
return nil, Parameter{}, err
}
return matchedNode.handler, Parameter{node: matchedNode, path: path}, err
}
func (router *router) AddRoute(method string, path string, handler HandlerFunc) {
var (
rootNode *node
ok bool
)
if rootNode, ok = router.trees[method]; !ok {
rootNode = &node{}
router.trees[method] = rootNode
}
parts, names := splitURLPath(path)
rootNode.addRoute(parts, names, handler)
if path == "/" {
} else if path[len(path)-1] != '/' {
parts, names := splitURLPath(path + "/")
rootNode.addRoute(parts, names, handler)
} else {
parts, names := splitURLPath(path[:len(path)-1])
rootNode.addRoute(parts, names, handler)
}
rootNode.optimizeRoutes()
}
//Parameter holds the parameters matched in the route
type Parameter struct {
*node // matched node
path string // url path given
cached map[string]string
}
//Len returns number arguments matched in the provided URL
func (p *Parameter) Len() int {
return len(p.names)
}
//ByName returns the url parameter by name
func (p *Parameter) ByName(name string) (string, error) {
if i, has := p.names[name]; has {
return p.findParam(i)
}
return "", fmt.Errorf("Parameter not found")
}
func lastIndexByte(s string, c byte) int {
for i := len(s) - 1; i >= 0; i-- {
if s[i] == c {
return i
}
}
return -1
}
//findParam walks up the matched node looking for parameters returns the last parameter
func (p *Parameter) findParam(idx int) (string, error) {
index := len(p.names) - 1
urlPath := p.path
pathLen := len(p.path)
node := p.node
for node != nil {
if node.text[0] == ':' {
ctn := lastIndexByte(urlPath, '/')
if ctn == -1 {
return "", fmt.Errorf("Parameter not found")
}
pathLen = ctn + 1
if index == idx {
return urlPath[pathLen:], nil
}
index--
} else {
pathLen -= len(node.text)
}
urlPath = urlPath[0:pathLen]
node = node.parent
}
return "", fmt.Errorf("Parameter not found")
}