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