generated from maragudk/template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
router.go
117 lines (100 loc) · 2.97 KB
/
router.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
package clir
import (
"fmt"
"regexp"
"strings"
)
// Router for [Runner]-s which itself satisfies [Runner].
type Router struct {
middlewares []Middleware
patterns []*regexp.Regexp
runners map[string]Runner
}
func NewRouter() *Router {
return &Router{
runners: map[string]Runner{},
}
}
// Run satisfies [Runner].
func (r *Router) Run(ctx Context) error {
// Apply middlewares first, because they can modify the context, including the Context.Args to match against.
var called bool
var middlewareCtx Context
var runner Runner = RunnerFunc(func(ctx Context) error {
called = true
middlewareCtx = ctx
return nil
})
// Apply middlewares in reverse order, so the first middleware is the outermost one, to be called first.
for i := len(r.middlewares) - 1; i >= 0; i-- {
runner = r.middlewares[i](runner)
}
if err := runner.Run(ctx); err != nil {
return fmt.Errorf("error while applying middleware: %w", err)
}
if !called {
return nil
}
ctx = middlewareCtx
for _, pattern := range r.patterns {
if (len(ctx.Args) == 0 && pattern.String() == "^$") || (len(ctx.Args) > 0 && pattern.MatchString(ctx.Args[0])) {
runner = r.runners[pattern.String()]
if len(ctx.Args) > 0 {
ctx.Matches = pattern.FindStringSubmatch(ctx.Args[0])
ctx.Args = ctx.Args[1:]
}
return runner.Run(ctx)
}
}
//for _, router := range r.routers {
// if err := router.Run(ctx); err == nil {
// return err
// }
//}
return ErrorRouteNotFound
}
// Route a [Runner] with the given pattern.
// Routes are matched in the order they were added.
func (r *Router) Route(pattern string, runner Runner) {
if !strings.HasPrefix(pattern, "^") {
pattern = "^" + pattern
}
if !strings.HasSuffix(pattern, "$") {
pattern += "$"
}
if _, ok := r.runners[pattern]; ok {
panic("cannot add route which already exists")
}
r.patterns = append(r.patterns, regexp.MustCompile(pattern))
r.runners[pattern] = runner
}
// RouteFunc is like [Router.Route], but with a [RunnerFunc].
func (r *Router) RouteFunc(pattern string, runner RunnerFunc) {
r.Route(pattern, runner)
}
// Branch into a new [Router] with the given pattern.
func (r *Router) Branch(pattern string, cb func(r *Router)) {
newR := NewRouter()
cb(newR)
r.Route(pattern, newR)
}
// Scope into a new [Router].
// The middlewares from the parent router are used in the new router,
// but new middlewares within the scope are only added to the new router, not the parent router.
func (r *Router) Scope(cb func(r *Router)) {
panic("not implemented")
//newR := NewRouter()
//cb(newR)
//r.routers = append(r.routers, newR)
}
// Middleware for [Router.Use].
type Middleware = func(next Runner) Runner
// Use [Middleware] on the current branch of the [Router].
// If called in a [Scope], it will apply to all routes in that scope.
func (r *Router) Use(middlewares ...Middleware) {
if len(r.runners) > 0 {
panic("cannot add middlewares after adding routes")
}
r.middlewares = append(r.middlewares, middlewares...)
}
var _ Runner = (*Router)(nil)