-
Notifications
You must be signed in to change notification settings - Fork 0
/
path_matcher.go
106 lines (96 loc) · 2.51 KB
/
path_matcher.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
package stgin
import (
"errors"
"fmt"
"regexp"
"strings"
"unicode/utf8"
)
const (
intRegex = "[0-9]+"
floatRegex = "[+\\-]?(?:(?:0|[1-9]\\d*)(?:\\.\\d*)?|\\.\\d+)(?:\\d[eE][+\\-]?\\d+)?"
stringRegex = "[a-zA-Z0-9_-]+"
expectQueryParams = "(\\?.*)?"
)
var getPathParamSpecificationRegex = regexp.MustCompile("^(\\$[a-zA-Z0-9_-]+(:[a-z]{1,6})?)$")
func trimFirstRune(s string) string {
_, i := utf8.DecodeRuneInString(s)
return s[i:]
}
type pathMatcher struct {
key string
tpe string
correspondingRegex *regexp.Regexp
rawRegex string
}
type Params = []Param
type Param struct {
key string
value string
}
func getMatcher(key, tpe string) *pathMatcher {
var correspondingRegex string
switch tpe {
case "int":
correspondingRegex = fmt.Sprintf("(?P<%s>%s)", key, intRegex)
case "float":
correspondingRegex = fmt.Sprintf("(?P<%s>%s)", key, floatRegex)
default:
correspondingRegex = fmt.Sprintf("(?P<%s>%s)", key, stringRegex)
}
return &pathMatcher{
key: key,
tpe: tpe,
correspondingRegex: regexp.MustCompile(correspondingRegex),
rawRegex: correspondingRegex,
}
}
func getPatternCorrespondingRegex(pattern string) (*regexp.Regexp, error) {
pattern = normalizePath(pattern)
portions := strings.Split(pattern, "/")
rawPatternRegex := ""
for i, portion := range portions {
isPathParamSpecification := getPathParamSpecificationRegex.Match([]byte(portion))
if !isPathParamSpecification {
rawPatternRegex += portion
} else {
keyAndType := strings.SplitN(portion, ":", 2)
var key = trimFirstRune(keyAndType[0])
var tpe string
if len(keyAndType) == 1 {
tpe = "string"
} else {
tpe = keyAndType[1]
}
matcher := getMatcher(key, tpe)
rawPatternRegex += matcher.rawRegex
}
if i != len(portions)-1 {
rawPatternRegex += "/"
}
}
regex, compileErr := regexp.Compile("^" + rawPatternRegex + expectQueryParams + "$")
if compileErr != nil {
return nil, errors.New(fmt.Sprintf("could not compile '%s' as a valid uri pattern", pattern))
} else {
return regex, nil
}
}
func MatchAndExtractPathParams(route *Route, uri string) ([]Param, bool) {
regex := route.correspondingRegex
if !regex.Match([]byte(uri)) {
return nil, false
} else {
match := regex.FindStringSubmatch(uri)
var res Params
for i, name := range regex.SubexpNames() {
if i != 0 && name != "" {
res = append(res, Param{
key: name,
value: match[i],
})
}
}
return res, true
}
}