-
Notifications
You must be signed in to change notification settings - Fork 0
/
route.go
141 lines (121 loc) · 3.05 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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Copyright (c) 2021 Hikaru Miyahara
// Copyright (c) 2012-2018 The Gorilla Authors.
package tinyrouter
import (
"errors"
"fmt"
"net/http"
"regexp"
"strings"
)
var (
InvalidPathError = errors.New("tinyrouter: invalid path")
UnbalancedBracesError = errors.New("tinyrouter: unbalanced braces")
)
type route struct {
paramNames []string
method string
path string
pattern *regexp.Regexp
handleFunc http.HandlerFunc
}
func extractParam(path string, indexList []int) []string {
paramList := make([]string, 0)
for i := 0; i < len(indexList); i += 2 {
paramList = append(paramList, path[indexList[i]+1:indexList[i+1]])
}
return paramList
}
func makePatternString(path string, indexList []int) string {
var builder strings.Builder
if len(indexList) == 0 {
builder.WriteString(path)
} else {
var start int = 0
var next int
for i := 0; i < len(indexList); i += 2 {
next = indexList[i]
builder.WriteString(path[start:next])
builder.WriteString("([^/]+)")
start = indexList[i+1] + 1
}
}
return fmt.Sprintf("^%s[/]?$", builder.String())
}
func makePattern(path string, indexList []int) (*regexp.Regexp, error) {
pstring := makePatternString(path, indexList)
return regexp.Compile(pstring)
}
func normalize(raw string) (string, error) {
if raw[0] != '/' {
return "", InvalidPathError
}
if raw[len(raw)-1] == '/' {
return raw[:len(raw)-1], nil
}
return raw, nil
}
func bracesIndex(path string) ([]int, error) {
indexList := make([]int, 0)
checkBraces := 0
for i := 0; i < len(path); i++ {
switch path[i] {
case '{':
if checkBraces++; checkBraces != 1 {
return nil, UnbalancedBracesError
}
indexList = append(indexList, i)
case '}':
if checkBraces--; checkBraces != 0 {
return nil, UnbalancedBracesError
}
indexList = append(indexList, i)
}
}
return indexList, nil
}
func newRoute(method string, raw string, f func(http.ResponseWriter, *http.Request)) (*route, error) {
path, pathErr := normalize(raw)
if pathErr != nil {
return nil, pathErr
}
indexList, bracesErr := bracesIndex(path)
if bracesErr != nil {
return nil, bracesErr
}
pattern, patternErr := makePattern(path, indexList)
if patternErr != nil {
return nil, patternErr
}
return &route{
paramNames: extractParam(path, indexList),
method: method,
path: raw,
pattern: pattern,
handleFunc: f,
}, nil
}
func (m *route) match(r *http.Request) bool {
matchMethod := m.method == r.Method
matchPath := m.pattern.MatchString(r.URL.Path)
return matchMethod && matchPath
}
func (m *route) params(r *http.Request) []string {
result := m.pattern.FindStringSubmatch(r.URL.Path)
return result[1:]
}
func (m *route) combineParams(paramValues []string, paramBox map[string]interface{}) {
if len(m.paramNames) != len(paramValues) {
return
}
for index, name := range m.paramNames {
paramBox[name] = paramValues[index]
}
}
func (m *route) setParams(r *http.Request, paramBox map[string]interface{}) {
if len(m.paramNames) == 0 {
return
}
paramValues := m.params(r)
m.combineParams(paramValues, paramBox)
}