forked from coregx/fursy
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgroup.go
More file actions
155 lines (135 loc) · 5.43 KB
/
Copy pathgroup.go
File metadata and controls
155 lines (135 loc) · 5.43 KB
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// Copyright 2025 coregx. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package fursy
// RouteGroup represents a group of routes that share the same path prefix and middleware.
// Groups allow organizing routes hierarchically and applying middleware to specific route sets.
//
// Example:
//
// api := router.Group("/api")
// api.Use(AuthMiddleware())
//
// v1 := api.Group("/v1")
// v1.Handle("GET", "/users", listUsers) // GET /api/v1/users
// v1.Handle("POST", "/users", createUser) // POST /api/v1/users
//
// v2 := api.Group("/v2")
// v2.Handle("GET", "/users", listUsersV2) // GET /api/v2/users
type RouteGroup struct {
// prefix is the path prefix for all routes in this group.
prefix string
// router is a reference to the parent router.
router *Router
// middleware stores group-specific middleware.
// These are combined with router middleware when registering routes.
middleware []HandlerFunc
}
// Use registers middleware to the route group.
// Group middleware is executed after router middleware but before route handlers.
//
// Middleware order: Router.Use() → Group.Use() → Handler
//
// Example:
//
// api := router.Group("/api")
// api.Use(LoggerMiddleware())
// api.Use(AuthMiddleware())
//
// Can be chained:
//
// api.Use(Logger()).Use(Auth())
func (g *RouteGroup) Use(middleware ...HandlerFunc) *RouteGroup {
g.middleware = append(g.middleware, middleware...)
return g
}
// Group creates a new nested route group with the given prefix and optional middleware.
// The new group's prefix is the combination of the parent prefix and the new prefix.
// If no middleware is provided, the new group inherits the parent's middleware.
//
// Example:
//
// api := router.Group("/api")
// api.Use(LoggerMiddleware())
//
// v1 := api.Group("/v1") // Inherits logger
// v1.Use(AuthMiddleware()) // Adds auth
// v1.Handle("GET", "/users", handler) // GET /api/v1/users (logger + auth)
//
// v2 := api.Group("/v2", RateLimitMiddleware()) // Custom middleware
// v2.Handle("GET", "/users", handler) // GET /api/v2/users (ratelimit only)
func (g *RouteGroup) Group(prefix string, middleware ...HandlerFunc) *RouteGroup {
// If no middleware provided, inherit from parent group
// Always inherit parent middleware, then append child-specific.
groupMiddleware := make([]HandlerFunc, len(g.middleware), len(g.middleware)+len(middleware))
copy(groupMiddleware, g.middleware)
groupMiddleware = append(groupMiddleware, middleware...)
return &RouteGroup{
prefix: g.prefix + prefix,
router: g.router,
middleware: groupMiddleware,
}
}
// GET registers a type-safe GET route on the group.
// Type parameters are inferred from the handler signature.
func (g *RouteGroup) GET[Req, Res any](path string, handler Handler[Req, Res]) {
g.Handle("GET", path, adaptGenericHandler(handler))
}
// POST registers a type-safe POST route on the group.
func (g *RouteGroup) POST[Req, Res any](path string, handler Handler[Req, Res]) {
g.Handle("POST", path, adaptGenericHandler(handler))
}
// PUT registers a type-safe PUT route on the group.
func (g *RouteGroup) PUT[Req, Res any](path string, handler Handler[Req, Res]) {
g.Handle("PUT", path, adaptGenericHandler(handler))
}
// DELETE registers a type-safe DELETE route on the group.
func (g *RouteGroup) DELETE[Req, Res any](path string, handler Handler[Req, Res]) {
g.Handle("DELETE", path, adaptGenericHandler(handler))
}
// PATCH registers a type-safe PATCH route on the group.
func (g *RouteGroup) PATCH[Req, Res any](path string, handler Handler[Req, Res]) {
g.Handle("PATCH", path, adaptGenericHandler(handler))
}
// HEAD registers a type-safe HEAD route on the group.
func (g *RouteGroup) HEAD[Req, Res any](path string, handler Handler[Req, Res]) {
g.Handle("HEAD", path, adaptGenericHandler(handler))
}
// OPTIONS registers a type-safe OPTIONS route on the group.
func (g *RouteGroup) OPTIONS[Req, Res any](path string, handler Handler[Req, Res]) {
g.Handle("OPTIONS", path, adaptGenericHandler(handler))
}
// Handle registers a route with the given HTTP method, path, and handler.
// This is the core method used by all HTTP method shortcuts (GET, POST, etc.).
//
// The final route path is: group.prefix + path
// The final middleware chain is: router.middleware + group.middleware + handler
//
// Example:
//
// api := router.Group("/api")
// api.Handle("GET", "/users", handler) // Registers GET /api/users
func (g *RouteGroup) Handle(method, path string, handler HandlerFunc) {
// Combine group prefix with route path
fullPath := g.prefix + path
// Combine group middleware + handler into a slice
groupHandlers := g.combineMiddleware(handler)
// Register route on parent router with group handlers
// The router will combine its own middleware with these handlers in ServeHTTP
g.router.handleWithGroupMiddleware(method, fullPath, groupHandlers)
}
// combineMiddleware combines group middleware and the handler.
// Returns a slice of handlers ready to be merged with router middleware.
//
// Order: group.middleware → handler.
func (g *RouteGroup) combineMiddleware(handler HandlerFunc) []HandlerFunc {
// If group has no middleware, return just the handler
if len(g.middleware) == 0 {
return []HandlerFunc{handler}
}
// Combine group middleware + handler
combined := make([]HandlerFunc, len(g.middleware)+1)
copy(combined, g.middleware)
combined[len(g.middleware)] = handler
return combined
}