forked from xujiajun/gorouter
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtree.go
140 lines (113 loc) · 2.67 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
130
131
132
133
134
135
136
137
138
139
140
package gorouter
import (
"net/http"
"strings"
)
type (
// Tree records node
Tree struct {
root *Node
}
// Node records any URL params, and executes an end handler.
Node struct {
key string
// path records a request path
path string
handle http.HandlerFunc
// depth records Node's depth
depth int
// children records Node's children node
children map[string]*Node
// isLeaf flag
isLeaf bool
// middleware records middleware stack
middleware []middlewareType
}
)
// NewNode returns a newly initialized Node object that implements the Node
func NewNode(key string, depth int) *Node {
return &Node{
key: key,
depth: depth,
children: make(map[string]*Node),
}
}
// NewTree returns a newly initialized Tree object that implements the Tree
func NewTree() *Tree {
return &Tree{
root: NewNode("/", 1),
}
}
// Add use `pattern` 、handle、middleware stack as node register to tree
func (tree *Tree) Add(pattern string, handle http.HandlerFunc, middleware ...middlewareType) {
var parent = tree.root
if pattern != parent.key {
pattern = trimPathPrefix(pattern)
res := splitPattern(pattern)
for _, key := range res {
node, ok := parent.children[key]
if !ok {
node = NewNode(key, parent.depth+1)
if len(middleware) > 0 {
node.middleware = append(node.middleware, middleware...)
}
parent.children[key] = node
}
parent = node
}
}
if len(middleware) > 0 && parent.depth == 1 {
parent.middleware = append(parent.middleware, middleware...)
}
parent.handle = handle
parent.isLeaf = true
parent.path = pattern
}
// Find returns nodes that the request match the route pattern
func (tree *Tree) Find(pattern string, isRegex int) (nodes []*Node) {
var (
node = tree.root
queue []*Node
)
if pattern == node.path {
nodes = append(nodes, node)
return
}
if isRegex == 0 {
pattern = trimPathPrefix(pattern)
}
res := splitPattern(pattern)
for _, key := range res {
child, ok := node.children[key]
if !ok {
return
}
if pattern == child.path && isRegex == 0 {
nodes = append(nodes, child)
return
}
node = child
}
queue = append(queue, node)
for len(queue) > 0 {
var queueTemp []*Node
for _, n := range queue {
if n.isLeaf {
nodes = append(nodes, n)
}
for _, vnode := range n.children {
queueTemp = append(queueTemp, vnode)
}
}
queue = queueTemp
}
return
}
// trimPathPrefix is short for strings.TrimPrefix with param prefix `/`
func trimPathPrefix(pattern string) string {
return strings.TrimPrefix(pattern, "/")
}
// splitPattern is short for strings.Split with param seq `/`
func splitPattern(pattern string) []string {
return strings.Split(pattern, "/")
}