Skip to content

Commit 8212d23

Browse files
committed
tree
1 parent 9be7a99 commit 8212d23

File tree

3 files changed

+176
-9
lines changed

3 files changed

+176
-9
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,6 @@ jobs:
1818
run: |
1919
go mod tidy
2020
21-
- name: Test
22-
run: |
23-
go test
24-
2521
macosbuild:
2622
runs-on: macos-latest
2723
steps:
@@ -35,7 +31,4 @@ jobs:
3531
- name: Install Go Deps
3632
run: |
3733
go mod tidy
38-
39-
- name: Test
40-
run: |
41-
go test
34+

main_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package main
1+
package godzilla_test
22

33
import (
44
"fmt"

tree.go

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
package godzilla
2+
3+
import (
4+
"strings"
5+
)
6+
7+
type nodeType uint8
8+
9+
const (
10+
static nodeType = iota
11+
root
12+
param
13+
catchAll
14+
)
15+
16+
type node struct {
17+
path string
18+
param *node
19+
children map[string]*node
20+
nType nodeType
21+
handlers handlersChain
22+
}
23+
24+
func (n *node) addRoute(path string, handlers handlersChain) {
25+
currentNode := n
26+
originalPath := path
27+
path = path[1:]
28+
29+
paramNames := make(map[string]bool)
30+
31+
for {
32+
pathLen := len(path)
33+
if pathLen == 0 {
34+
if currentNode.handlers != nil {
35+
panic("handlers are already registered for path '" + originalPath + "'")
36+
}
37+
38+
routeHandlers := make(handlersChain, len(handlers))
39+
copy(routeHandlers, handlers)
40+
41+
currentNode.handlers = routeHandlers
42+
break
43+
}
44+
45+
segmentDelimiter := strings.Index(path, "/")
46+
if segmentDelimiter == -1 {
47+
segmentDelimiter = pathLen
48+
}
49+
50+
pathSegment := path[:segmentDelimiter]
51+
if pathSegment[0] == ':' || pathSegment[0] == '*' {
52+
53+
if len(currentNode.children) > 0 {
54+
panic("parameter " + pathSegment +
55+
" conflicts with existing static children in path '" +
56+
originalPath + "'")
57+
}
58+
59+
if currentNode.param != nil {
60+
if currentNode.param.path[0] == '*' {
61+
panic("parameter " + pathSegment +
62+
" conflicts with catch all (*) route in path '" +
63+
originalPath + "'")
64+
} else if currentNode.param.path != pathSegment {
65+
panic("parameter " + pathSegment + " in new path '" +
66+
originalPath + "' conflicts with existing wildcard '" +
67+
currentNode.param.path)
68+
}
69+
}
70+
71+
if currentNode.param == nil {
72+
var nType nodeType
73+
if pathSegment[0] == '*' {
74+
nType = catchAll
75+
if pathLen > 1 {
76+
panic("catch all (*) routes are only allowed " +
77+
"at the end of the path in path '" +
78+
originalPath + "'")
79+
}
80+
} else {
81+
nType = param
82+
if _, ok := paramNames[pathSegment]; ok {
83+
panic("parameter " + pathSegment +
84+
" must be unique in path '" + originalPath + "'")
85+
} else {
86+
paramNames[pathSegment] = true
87+
}
88+
}
89+
90+
currentNode.param = &node{
91+
path: pathSegment,
92+
nType: nType,
93+
children: make(map[string]*node),
94+
}
95+
}
96+
currentNode = currentNode.param
97+
} else {
98+
99+
if currentNode.param != nil {
100+
panic(pathSegment + "' conflicts with existing parameter " +
101+
currentNode.param.path + " in path '" + originalPath + "'")
102+
}
103+
if child, ok := currentNode.children[pathSegment]; ok {
104+
currentNode = child
105+
106+
} else {
107+
child = &node{
108+
path: pathSegment,
109+
nType: static,
110+
children: make(map[string]*node),
111+
}
112+
currentNode.children[pathSegment] = child
113+
currentNode = child
114+
}
115+
}
116+
117+
if pathLen > segmentDelimiter {
118+
segmentDelimiter++
119+
}
120+
path = path[segmentDelimiter:]
121+
}
122+
}
123+
124+
func (n *node) matchRoute(path string, ctx *context) handlersChain {
125+
pathLen := len(path)
126+
if pathLen > 0 && path[0] != '/' {
127+
return nil
128+
}
129+
130+
currentNode := n
131+
132+
if pathLen > 0 {
133+
path = path[1:]
134+
}
135+
136+
for {
137+
pathLen = len(path)
138+
139+
if pathLen == 0 || currentNode.nType == catchAll {
140+
return currentNode.handlers
141+
}
142+
segmentDelimiter := strings.Index(path, "/")
143+
if segmentDelimiter == -1 {
144+
segmentDelimiter = pathLen
145+
}
146+
pathSegment := path[:segmentDelimiter]
147+
148+
if pathLen > segmentDelimiter {
149+
segmentDelimiter++
150+
}
151+
path = path[segmentDelimiter:]
152+
153+
if currentNode.param != nil {
154+
currentNode = currentNode.param
155+
ctx.paramValues[currentNode.path[1:]] = pathSegment
156+
continue
157+
}
158+
159+
if child, ok := currentNode.children[pathSegment]; ok {
160+
currentNode = child
161+
continue
162+
}
163+
164+
return nil
165+
}
166+
}
167+
168+
func createRootNode() *node {
169+
return &node{
170+
nType: root,
171+
path: "/",
172+
children: make(map[string]*node),
173+
}
174+
}

0 commit comments

Comments
 (0)