/
route.go
118 lines (91 loc) · 2.19 KB
/
route.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
package ape
import (
"regexp"
"strings"
)
const (
defaultConstraint = "[^/]+?"
formantConstraint = "(?:\\.(?P<_format>[a-z0-9_]+))?"
NumberOnlyConstraint = "[0-9]+"
)
var (
namedSegmentRegexp = regexp.MustCompile(":[a-z][a-z0-9_]*")
)
type Route struct {
Verbs []string
Path string
Handler Handler
Constraints map[string]string
regexp *regexp.Regexp
mounted bool
}
func NewRoute(verbs []string, path string, handler Handler) *Route {
route := &Route{
Verbs: verbs,
Path: path,
Handler: handler,
Constraints: map[string]string{},
mounted: false,
}
route.compile()
return route
}
func (r *Route) Match(verb string, path string) bool {
verb = strings.ToUpper(verb)
verbHit := false
for _, expectedVerb := range r.Verbs {
if expectedVerb == verb {
verbHit = true
break
}
}
return verbHit && r.regexp.MatchString(path)
}
func (r *Route) Params(path string) map[string]string {
params := map[string]string{}
segments := r.regexp.SubexpNames()
segmentParams := r.regexp.FindAllStringSubmatch(path, -1)[0]
for i, segment := range segments {
if i == 0 {
continue
}
params[segment] = segmentParams[i]
}
return params
}
func (r *Route) Constrain(key, val string) {
r.Constraints[key] = val
r.compile()
}
func (r *Route) Mounted() *Route {
r.mounted = true
r.compile()
return r
}
func (r *Route) String() string {
verbs := strings.Join(r.Verbs, "|")
return "[" + verbs + "] " + r.Path
}
func (r *Route) compile() {
segments := strings.Split(r.Path, "/")
compiledSegments := make([]string, len(segments))
for i, segment := range segments {
switch {
case namedSegmentRegexp.MatchString(segment):
segmentName := regexp.QuoteMeta(segment[1:len(segment)])
constraint := defaultConstraint
if _, hit := r.Constraints[segmentName]; hit {
constraint = r.Constraints[segmentName]
}
segment = "(?P<" + segmentName + ">" + constraint + ")"
default:
segment = regexp.QuoteMeta(segment)
}
compiledSegments[i] = segment
}
regexpSource := "^" + strings.Join(compiledSegments, "/") + formantConstraint
if !r.mounted {
regexpSource += "$"
}
r.regexp = regexp.MustCompile(regexpSource)
}