-
Notifications
You must be signed in to change notification settings - Fork 4
/
pretree.go
205 lines (186 loc) · 4.29 KB
/
pretree.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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
/*
Copyright (c) 2021 ffactory.org
pretree is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
*/
package pretree
import (
"strings"
)
const (
MethodGet = "GET"
MethodHead = "HEAD"
MethodPost = "POST"
MethodPut = "PUT"
MethodPatch = "PATCH"
MethodDelete = "DELETE"
MethodConnect = "CONNECT"
MethodOptions = "OPTIONS"
MethodTrace = "TRACE"
)
type PreTree struct {
treeGroup map[string]*Tree
}
// 初始化对象
//
// Initialize object
func NewPreTree() *PreTree {
p := &PreTree{}
methods := []string{"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "CONNECT", "OPTIONS", "TRACE"}
p.treeGroup = make(map[string]*Tree)
for _, method := range methods {
tree := newTree()
tree.name = string(method)
p.treeGroup[method] = tree
}
return p
}
// 存储路由规则
//
// Store routing rules
func (p *PreTree) Store(method, urlRule string) {
t := p.treeGroup[method]
t.insert(urlRule)
}
// 查询URL匹配的树节点并返回变量
//
// Query the tree node with matching URL and return variables
func (p *PreTree) Query(method, urlPath string) (isExist bool, rule string, vars map[string]string) {
t := p.treeGroup[method]
isExist, node, vars := t.match(urlPath)
if isExist {
return true, node.Rule(), vars
} else {
return false, "", vars
}
}
// 前缀树数据结构
//
// Prefix tree data structure
type Tree struct {
rule string
name string
nodes []*Tree
isEnd bool
isVariable bool
}
func newTree() *Tree {
return &Tree{}
}
func (t *Tree) appendChild(child *Tree) {
t.nodes = append(t.nodes, child)
}
// 获取子节点
//
// Get child nodes
func (t *Tree) Child() []*Tree {
return t.nodes
}
// 获取当前节点的路由规则
//
// Get the routing rule of the current node
func (t *Tree) Rule() string {
return t.rule
}
// 获取当前节点的名称
//
// Get the name of the current node
func (t *Tree) Name() string {
return t.name
}
// 获取当前节点的变量名
//
// Get the variable name of the current node
// :id => id
func (t *Tree) VarName() string {
return strings.TrimPrefix(t.name, ":")
}
func (t *Tree) insert(urlRule string) {
current := t
list := parsePath(urlRule)
for _, word := range list {
isExist := false
// 如果已经存在路径,继续匹配子节点
for _, n := range current.Child() {
if n.name == word {
isExist = true
current = n
break
}
}
// 已存在进入下一次循环
if isExist {
continue
}
// 不存在的路径新增
node := newTree()
node.name = word
// 记录本路径是否变量
if isVariable(word) {
node.isVariable = true
}
current.appendChild(node)
current = node
}
current.rule = urlRule
current.isEnd = true
}
func (t *Tree) match(urlPath string) (bool, *Tree, map[string]string) {
// vars 用于存储路由变量数据
vars := make(map[string]string)
current := t
list := parsePath(urlPath)
for index, word := range list {
isExist := false
hasVar := false
for _, n := range current.Child() {
if n.name == word {
hasVar = false
isExist = true
current = n
break
}
}
if isExist {
continue
}
// 第二个路径匹配不到情况下,查找是否有变量路径,继续从变量路径往下找
for _, m := range current.Child() {
if m.isVariable && index > 0 && !hasVar {
hasVar = true
current = m
vars[m.VarName()] = word
break
}
}
// 找到有变量路径,进入下一次循环
if hasVar {
continue
}
}
if current.isEnd {
return true, current, vars
} else {
return false, nil, nil
}
}
func parsePath(path string) []string {
path = formatRule(path)
return strings.Split(path, "/")
}
func formatRule(rule string) string {
rule = strings.ReplaceAll(rule, "{", ":")
rule = strings.ReplaceAll(rule, "}", "")
rule = strings.TrimPrefix(rule, "/")
rule = strings.TrimSuffix(rule, "/")
return rule
}
func isVariable(s string) bool {
return strings.HasPrefix(s, ":")
}