1use crate::Result;
4use std::collections::HashMap;
5
6#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Deserialize, serde::Serialize)]
8#[serde(rename_all = "lowercase")]
9pub enum HttpMethod {
10 GET,
11 POST,
12 PUT,
13 DELETE,
14 PATCH,
15 HEAD,
16 OPTIONS,
17}
18
19#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
21pub struct Route {
22 pub method: HttpMethod,
24 pub path: String,
26 pub priority: i32,
28 pub metadata: HashMap<String, serde_json::Value>,
30}
31
32impl Route {
33 pub fn new(method: HttpMethod, path: String) -> Self {
35 Self {
36 method,
37 path,
38 priority: 0,
39 metadata: HashMap::new(),
40 }
41 }
42
43 pub fn with_priority(mut self, priority: i32) -> Self {
45 self.priority = priority;
46 self
47 }
48
49 pub fn with_metadata(mut self, key: String, value: serde_json::Value) -> Self {
51 self.metadata.insert(key, value);
52 self
53 }
54}
55
56#[derive(Debug, Clone)]
58pub struct RouteRegistry {
59 http_routes: HashMap<HttpMethod, Vec<Route>>,
61 ws_routes: Vec<Route>,
63 grpc_routes: HashMap<String, Vec<Route>>,
65}
66
67impl RouteRegistry {
68 pub fn new() -> Self {
70 Self {
71 http_routes: HashMap::new(),
72 ws_routes: Vec::new(),
73 grpc_routes: HashMap::new(),
74 }
75 }
76
77 pub fn add_http_route(&mut self, route: Route) -> Result<()> {
79 self.http_routes.entry(route.method.clone()).or_default().push(route);
80 Ok(())
81 }
82
83 pub fn add_ws_route(&mut self, route: Route) -> Result<()> {
85 self.ws_routes.push(route);
86 Ok(())
87 }
88
89 pub fn clear(&mut self) {
91 self.http_routes.clear();
92 self.ws_routes.clear();
93 self.grpc_routes.clear();
94 }
95
96 pub fn add_route(&mut self, route: Route) -> Result<()> {
98 self.add_http_route(route)
99 }
100
101 pub fn add_grpc_route(&mut self, service: String, route: Route) -> Result<()> {
103 self.grpc_routes.entry(service).or_default().push(route);
104 Ok(())
105 }
106
107 pub fn find_http_routes(&self, method: &HttpMethod, path: &str) -> Vec<&Route> {
109 self.http_routes
110 .get(method)
111 .map(|routes| {
112 routes.iter().filter(|route| self.matches_path(&route.path, path)).collect()
113 })
114 .unwrap_or_default()
115 }
116
117 pub fn find_ws_routes(&self, path: &str) -> Vec<&Route> {
119 self.ws_routes
120 .iter()
121 .filter(|route| self.matches_path(&route.path, path))
122 .collect()
123 }
124
125 pub fn find_grpc_routes(&self, service: &str, method: &str) -> Vec<&Route> {
127 self.grpc_routes
128 .get(service)
129 .map(|routes| {
130 routes.iter().filter(|route| self.matches_path(&route.path, method)).collect()
131 })
132 .unwrap_or_default()
133 }
134
135 fn matches_path(&self, pattern: &str, path: &str) -> bool {
137 if pattern == path {
138 return true;
139 }
140
141 if pattern.contains('*') {
143 let pattern_parts: Vec<&str> = pattern.split('/').collect();
144 let path_parts: Vec<&str> = path.split('/').collect();
145
146 if pattern_parts.len() != path_parts.len() {
147 return false;
148 }
149
150 for (pattern_part, path_part) in pattern_parts.iter().zip(path_parts.iter()) {
151 if *pattern_part != "*" && *pattern_part != *path_part {
152 return false;
153 }
154 }
155 return true;
156 }
157
158 false
159 }
160
161 pub fn get_http_routes(&self, method: &HttpMethod) -> Vec<&Route> {
163 self.http_routes
164 .get(method)
165 .map(|routes| routes.iter().collect())
166 .unwrap_or_default()
167 }
168
169 pub fn get_ws_routes(&self) -> Vec<&Route> {
171 self.ws_routes.iter().collect()
172 }
173
174 pub fn get_grpc_routes(&self, service: &str) -> Vec<&Route> {
176 self.grpc_routes
177 .get(service)
178 .map(|routes| routes.iter().collect())
179 .unwrap_or_default()
180 }
181}
182
183impl Default for RouteRegistry {
184 fn default() -> Self {
185 Self::new()
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192
193 #[test]
194 fn test_route_new() {
195 let route = Route::new(HttpMethod::GET, "/api/users".to_string());
196 assert_eq!(route.method, HttpMethod::GET);
197 assert_eq!(route.path, "/api/users");
198 assert_eq!(route.priority, 0);
199 assert!(route.metadata.is_empty());
200 }
201
202 #[test]
203 fn test_route_with_priority() {
204 let route = Route::new(HttpMethod::POST, "/api/users".to_string()).with_priority(10);
205 assert_eq!(route.priority, 10);
206 }
207
208 #[test]
209 fn test_route_with_metadata() {
210 let route = Route::new(HttpMethod::GET, "/api/users".to_string())
211 .with_metadata("version".to_string(), serde_json::json!("v1"))
212 .with_metadata("auth".to_string(), serde_json::json!(true));
213
214 assert_eq!(route.metadata.get("version"), Some(&serde_json::json!("v1")));
215 assert_eq!(route.metadata.get("auth"), Some(&serde_json::json!(true)));
216 }
217
218 #[test]
219 fn test_route_registry_new() {
220 let registry = RouteRegistry::new();
221 assert!(registry.http_routes.is_empty());
222 assert!(registry.ws_routes.is_empty());
223 assert!(registry.grpc_routes.is_empty());
224 }
225
226 #[test]
227 fn test_route_registry_default() {
228 let registry = RouteRegistry::default();
229 assert!(registry.http_routes.is_empty());
230 }
231
232 #[test]
233 fn test_add_http_route() {
234 let mut registry = RouteRegistry::new();
235 let route = Route::new(HttpMethod::GET, "/api/users".to_string());
236
237 assert!(registry.add_http_route(route).is_ok());
238 assert_eq!(registry.get_http_routes(&HttpMethod::GET).len(), 1);
239 }
240
241 #[test]
242 fn test_add_multiple_http_routes() {
243 let mut registry = RouteRegistry::new();
244
245 registry
246 .add_http_route(Route::new(HttpMethod::GET, "/api/users".to_string()))
247 .unwrap();
248 registry
249 .add_http_route(Route::new(HttpMethod::GET, "/api/posts".to_string()))
250 .unwrap();
251 registry
252 .add_http_route(Route::new(HttpMethod::POST, "/api/users".to_string()))
253 .unwrap();
254
255 assert_eq!(registry.get_http_routes(&HttpMethod::GET).len(), 2);
256 assert_eq!(registry.get_http_routes(&HttpMethod::POST).len(), 1);
257 }
258
259 #[test]
260 fn test_add_ws_route() {
261 let mut registry = RouteRegistry::new();
262 let route = Route::new(HttpMethod::GET, "/ws/chat".to_string());
263
264 assert!(registry.add_ws_route(route).is_ok());
265 assert_eq!(registry.get_ws_routes().len(), 1);
266 }
267
268 #[test]
269 fn test_add_grpc_route() {
270 let mut registry = RouteRegistry::new();
271 let route = Route::new(HttpMethod::POST, "GetUser".to_string());
272
273 assert!(registry.add_grpc_route("UserService".to_string(), route).is_ok());
274 assert_eq!(registry.get_grpc_routes("UserService").len(), 1);
275 }
276
277 #[test]
278 fn test_add_route_alias() {
279 let mut registry = RouteRegistry::new();
280 let route = Route::new(HttpMethod::GET, "/api/test".to_string());
281
282 assert!(registry.add_route(route).is_ok());
283 assert_eq!(registry.get_http_routes(&HttpMethod::GET).len(), 1);
284 }
285
286 #[test]
287 fn test_clear() {
288 let mut registry = RouteRegistry::new();
289
290 registry
291 .add_http_route(Route::new(HttpMethod::GET, "/api/users".to_string()))
292 .unwrap();
293 registry
294 .add_ws_route(Route::new(HttpMethod::GET, "/ws/chat".to_string()))
295 .unwrap();
296 registry
297 .add_grpc_route(
298 "Service".to_string(),
299 Route::new(HttpMethod::POST, "Method".to_string()),
300 )
301 .unwrap();
302
303 assert!(!registry.get_http_routes(&HttpMethod::GET).is_empty());
304 assert!(!registry.get_ws_routes().is_empty());
305
306 registry.clear();
307
308 assert!(registry.get_http_routes(&HttpMethod::GET).is_empty());
309 assert!(registry.get_ws_routes().is_empty());
310 assert!(registry.get_grpc_routes("Service").is_empty());
311 }
312
313 #[test]
314 fn test_find_http_routes_exact_match() {
315 let mut registry = RouteRegistry::new();
316 registry
317 .add_http_route(Route::new(HttpMethod::GET, "/api/users".to_string()))
318 .unwrap();
319
320 let found = registry.find_http_routes(&HttpMethod::GET, "/api/users");
321 assert_eq!(found.len(), 1);
322 assert_eq!(found[0].path, "/api/users");
323 }
324
325 #[test]
326 fn test_find_http_routes_no_match() {
327 let mut registry = RouteRegistry::new();
328 registry
329 .add_http_route(Route::new(HttpMethod::GET, "/api/users".to_string()))
330 .unwrap();
331
332 let found = registry.find_http_routes(&HttpMethod::GET, "/api/posts");
333 assert_eq!(found.len(), 0);
334 }
335
336 #[test]
337 fn test_find_http_routes_wildcard_match() {
338 let mut registry = RouteRegistry::new();
339 registry
340 .add_http_route(Route::new(HttpMethod::GET, "/api/*/details".to_string()))
341 .unwrap();
342
343 let found = registry.find_http_routes(&HttpMethod::GET, "/api/users/details");
344 assert_eq!(found.len(), 1);
345
346 let found = registry.find_http_routes(&HttpMethod::GET, "/api/posts/details");
347 assert_eq!(found.len(), 1);
348 }
349
350 #[test]
351 fn test_find_http_routes_wildcard_no_match_different_length() {
352 let mut registry = RouteRegistry::new();
353 registry
354 .add_http_route(Route::new(HttpMethod::GET, "/api/*/details".to_string()))
355 .unwrap();
356
357 let found = registry.find_http_routes(&HttpMethod::GET, "/api/users");
358 assert_eq!(found.len(), 0);
359 }
360
361 #[test]
362 fn test_find_ws_routes() {
363 let mut registry = RouteRegistry::new();
364 registry
365 .add_ws_route(Route::new(HttpMethod::GET, "/ws/chat".to_string()))
366 .unwrap();
367
368 let found = registry.find_ws_routes("/ws/chat");
369 assert_eq!(found.len(), 1);
370 }
371
372 #[test]
373 fn test_find_ws_routes_wildcard() {
374 let mut registry = RouteRegistry::new();
375 registry.add_ws_route(Route::new(HttpMethod::GET, "/ws/*".to_string())).unwrap();
376
377 let found = registry.find_ws_routes("/ws/chat");
378 assert_eq!(found.len(), 1);
379
380 let found = registry.find_ws_routes("/ws/notifications");
381 assert_eq!(found.len(), 1);
382 }
383
384 #[test]
385 fn test_find_grpc_routes() {
386 let mut registry = RouteRegistry::new();
387 registry
388 .add_grpc_route(
389 "UserService".to_string(),
390 Route::new(HttpMethod::POST, "GetUser".to_string()),
391 )
392 .unwrap();
393
394 let found = registry.find_grpc_routes("UserService", "GetUser");
395 assert_eq!(found.len(), 1);
396 }
397
398 #[test]
399 fn test_find_grpc_routes_wildcard() {
400 let mut registry = RouteRegistry::new();
401 registry
404 .add_grpc_route(
405 "UserService".to_string(),
406 Route::new(HttpMethod::POST, "GetUser".to_string()),
407 )
408 .unwrap();
409
410 let found = registry.find_grpc_routes("UserService", "GetUser");
411 assert_eq!(found.len(), 1);
412 }
413
414 #[test]
415 fn test_matches_path_exact() {
416 let registry = RouteRegistry::new();
417 assert!(registry.matches_path("/api/users", "/api/users"));
418 assert!(!registry.matches_path("/api/users", "/api/posts"));
419 }
420
421 #[test]
422 fn test_matches_path_wildcard_single_segment() {
423 let registry = RouteRegistry::new();
424 assert!(registry.matches_path("/api/*", "/api/users"));
425 assert!(registry.matches_path("/api/*", "/api/posts"));
426 assert!(!registry.matches_path("/api/*", "/api"));
427 assert!(!registry.matches_path("/api/*", "/api/users/123"));
428 }
429
430 #[test]
431 fn test_matches_path_wildcard_multiple_segments() {
432 let registry = RouteRegistry::new();
433 assert!(registry.matches_path("/api/*/details", "/api/users/details"));
434 assert!(registry.matches_path("/api/*/*", "/api/users/123"));
435 assert!(!registry.matches_path("/api/*/*", "/api/users"));
436 }
437
438 #[test]
439 fn test_get_http_routes_empty() {
440 let registry = RouteRegistry::new();
441 assert!(registry.get_http_routes(&HttpMethod::GET).is_empty());
442 }
443
444 #[test]
445 fn test_get_ws_routes_empty() {
446 let registry = RouteRegistry::new();
447 assert!(registry.get_ws_routes().is_empty());
448 }
449
450 #[test]
451 fn test_get_grpc_routes_empty() {
452 let registry = RouteRegistry::new();
453 assert!(registry.get_grpc_routes("Service").is_empty());
454 }
455
456 #[test]
457 fn test_http_method_serialization() {
458 let method = HttpMethod::GET;
459 let json = serde_json::to_string(&method).unwrap();
460 assert_eq!(json, r#""get""#);
461
462 let method = HttpMethod::POST;
463 let json = serde_json::to_string(&method).unwrap();
464 assert_eq!(json, r#""post""#);
465 }
466
467 #[test]
468 fn test_http_method_deserialization() {
469 let method: HttpMethod = serde_json::from_str(r#""get""#).unwrap();
470 assert_eq!(method, HttpMethod::GET);
471
472 let method: HttpMethod = serde_json::from_str(r#""post""#).unwrap();
473 assert_eq!(method, HttpMethod::POST);
474 }
475}