-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathmatcher.go
More file actions
277 lines (238 loc) · 5.62 KB
/
matcher.go
File metadata and controls
277 lines (238 loc) · 5.62 KB
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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
package rivet
import (
"fmt"
"net/http"
"regexp"
"strconv"
"strings"
)
// Matches 汇集以命名为 key 的 Matcher 生成器. 内建列表:
//
// string 字符串, 缺省值.
// alpha [a-zA-Z]+
// alnum [a-zA-Z0-9]+
// hex [a-fA-F0-9]+
// uint 可使用 strconv.ParseUint 进行转换, 支持 bitSize 参数
// int 可使用 strconv.ParseInt 进行转换, 支持 bitSize 参数
// reg 正则, 样例: ":id | ^id([0-9]+)$". 用 FindStringSubmatch 提取最后一个 Submatch.
//
// 其中: string, alpha, alnum, hex 可附加最小长度参数, 缺省值为 1.如:
// ":name string 10" 限制参数字符串字节长度不超过 10.
var Matches = map[string]func(string) Matcher{
"string": bString,
"alpha": bAlpha,
"alnum": bAlnum,
"hex": bHex,
"uint": bUint,
"int": bInt,
"reg": bRegexp,
}
var isOk = echo(echo)
// Ok 返回一个非 nil interface{}, 可被 Matcher 调用并返回该值, 表示匹配值就是原字符串,
// 那么 Trie 匹配到的 Argument.Vlaue 的值就是 nil. 目的是节省内存开销.
func Ok() interface{} {
return isOk
}
// Matcher 用于匹配, 转换 URL.Path 参数.
type Matcher interface {
// Matcher
//
// text 需要过滤的字符串, 例子:
// 路由 "/blog/cat:id uint".
// 路径 "/blog/cat3282".
// text 值是字符串 "3282".
//
// req 过滤器可能需要 Request 的信息.
//
// 返回值 val:
//
// nil 匹配失败.
// Ok() 匹配成功且 text 作为返回值保存至 Argument.Source.
// 其它 匹配成功, 保存至 Argument.Value.
Match(text string, req *http.Request) (val interface{})
}
// builder 以 exp 首段字符串为名字, 从 Matches 创建一个 Matcher.
// 该名字用于排序, 大值优先执行. 该值不能为空.
func builder(exp string) Matcher {
var m Matcher
args := strings.SplitN(exp, " ", 2)
if args[0] == "" {
args = []string{"string", strings.TrimSpace(exp)}
}
build := Matches[args[0]]
if build == nil {
args = []string{"reg", exp}
build = bRegexp
}
if build == nil {
panic(fmt.Sprintf("rivet: not exists in Matches with %#v", exp))
}
if len(args) == 2 {
m = build(args[1])
} else {
m = build(args[0])
}
if m == nil {
panic(fmt.Sprintf("rivet: want an Matcher, but got nil with %#v", exp))
}
return m
}
// MatchFun 包装一个函数为 Matcher. 参数 fn, 可以是以下类型:
//
// func(string) string
// func(string) interface{}
// func(string,*http.Request) string
// func(string,*http.Request) interface{}
func MatchFun(fn interface{}) (m Matcher, ok bool) {
ok = true
switch fn := fn.(type) {
case func(string) string:
m = ssMatch(fn)
case func(string) interface{}:
m = siMatch(fn)
case func(string, *http.Request) string:
m = srsMatch(fn)
case func(string, *http.Request) interface{}:
m = sriMatch(fn)
default:
ok = false
}
return
}
type ssMatch func(string) string
type siMatch func(string) interface{}
type srsMatch func(string, *http.Request) string
type sriMatch func(string, *http.Request) interface{}
func (fn ssMatch) Match(text string, req *http.Request) interface{} {
return fn(text)
}
func (fn siMatch) Match(text string, req *http.Request) interface{} {
return fn(text)
}
func (fn srsMatch) Match(text string, req *http.Request) interface{} {
return fn(text, req)
}
func (fn sriMatch) Match(text string, req *http.Request) interface{} {
return fn(text, req)
}
type mString int
type mAlpha int
type mAlnum int
type mHex int
type mUint int
type mInt int
type mRegexp regexp.Regexp
func minOne(s string) int {
if s == "" {
return 1
}
n, _ := strconv.Atoi(s)
return n
}
func bString(s string) Matcher {
return mString(minOne(s))
}
func bAlpha(s string) Matcher {
return mAlpha(minOne(s))
}
func bAlnum(s string) Matcher {
return mAlnum(minOne(s))
}
func bHex(s string) Matcher {
return mHex(minOne(s))
}
func bUint(s string) Matcher {
return mUint(minOne(s))
}
func bInt(s string) Matcher {
return mInt(minOne(s))
}
func bRegexp(s string) Matcher {
return (*mRegexp)(regexp.MustCompile(s))
}
func (n mString) Match(s string, _ *http.Request) interface{} {
if n != 0 && len(s) < int(n) {
return nil
}
return isOk
}
func (n mAlpha) Match(s string, _ *http.Request) interface{} {
if n != 0 && len(s) < int(n) {
return nil
}
for _, b := range []byte(s) {
if b < 'A' || b > 'z' || (b > 'Z' && b < 'a') {
return nil
}
}
return isOk
}
func (n mAlnum) Match(s string, _ *http.Request) interface{} {
if n != 0 && len(s) < int(n) {
return nil
}
a := []byte(s[1:])
b := s[0]
if b < 'A' || b > 'z' || (b > 'Z' && b < 'a') {
return nil
}
for _, b := range a {
if (b < '0' || b > '9') && b < 'A' || b > 'z' || (b > 'Z' && b < 'a') {
return nil
}
}
return isOk
}
func (n mUint) Match(s string, _ *http.Request) interface{} {
i, err := strconv.ParseUint(s, 10, int(n))
if err != nil {
return nil
}
switch n {
case 8:
return uint8(i)
case 16:
return uint16(i)
case 32:
return uint32(i)
case 64:
return uint64(i)
}
return i
}
func (n mInt) Match(s string, _ *http.Request) interface{} {
i, err := strconv.ParseInt(s, 10, int(n))
if err != nil {
return nil
}
switch n {
case 8:
return int8(i)
case 16:
return int16(i)
case 32:
return int32(i)
case 64:
return int64(i)
}
return i
}
func (n mHex) Match(s string, _ *http.Request) interface{} {
if n != 0 && len(s) < int(n) {
return nil
}
for _, b := range []byte(s) {
if (b < '0' || b > '9') && b < 'a' || b > 'f' {
return nil
}
}
return isOk
}
func (f *mRegexp) Match(s string, _ *http.Request) interface{} {
a := (*regexp.Regexp)(f).FindStringSubmatch(s)
size := len(a)
if size == 0 {
return nil
}
return a[size-1]
}