1use std::collections::HashMap;
7use std::sync::Arc;
8use webx_core::error::Result;
9use webx_core::http::IHttpContext;
10use webx_core::routing::{HttpMethod, IEndpoint, IRouter};
11
12type RouteValue = (Arc<dyn IEndpoint>, String);
13
14struct MethodRouter {
16 inner: matchit::Router<RouteValue>,
17}
18
19impl MethodRouter {
20 fn new() -> Self {
21 Self {
22 inner: matchit::Router::new(),
23 }
24 }
25
26 fn insert(&mut self, path: &str, endpoint: Arc<dyn IEndpoint>) {
27 let value = (endpoint, path.to_string());
28 if let Err(e) = self.inner.insert(path, value) {
29 tracing::warn!("[Router] Failed to register route '{}': {}", path, e);
30 }
31 }
32
33 fn at<'a>(&'a self, path: &'a str) -> Option<matchit::Match<'a, 'a, &'a RouteValue>> {
34 self.inner.at(path).ok()
35 }
36}
37
38pub struct Router {
40 get: MethodRouter,
41 post: MethodRouter,
42 put: MethodRouter,
43 delete: MethodRouter,
44 patch: MethodRouter,
45 head: MethodRouter,
46 options: MethodRouter,
47}
48
49impl Router {
50 pub fn new() -> Self {
51 Self {
52 get: MethodRouter::new(),
53 post: MethodRouter::new(),
54 put: MethodRouter::new(),
55 delete: MethodRouter::new(),
56 patch: MethodRouter::new(),
57 head: MethodRouter::new(),
58 options: MethodRouter::new(),
59 }
60 }
61
62 fn method_router_mut(&mut self, m: HttpMethod) -> &mut MethodRouter {
63 match m {
64 HttpMethod::Get => &mut self.get,
65 HttpMethod::Post => &mut self.post,
66 HttpMethod::Put => &mut self.put,
67 HttpMethod::Delete => &mut self.delete,
68 HttpMethod::Patch => &mut self.patch,
69 HttpMethod::Head => &mut self.head,
70 HttpMethod::Options => &mut self.options,
71 }
72 }
73
74 fn method_router(&self, m: HttpMethod) -> &MethodRouter {
75 match m {
76 HttpMethod::Get => &self.get,
77 HttpMethod::Post => &self.post,
78 HttpMethod::Put => &self.put,
79 HttpMethod::Delete => &self.delete,
80 HttpMethod::Patch => &self.patch,
81 HttpMethod::Head => &self.head,
82 HttpMethod::Options => &self.options,
83 }
84 }
85
86 pub fn path_exists(&self, path: &str) -> bool {
88 self.get.at(path).is_some()
89 || self.post.at(path).is_some()
90 || self.put.at(path).is_some()
91 || self.delete.at(path).is_some()
92 || self.patch.at(path).is_some()
93 || self.head.at(path).is_some()
94 || self.options.at(path).is_some()
95 }
96
97 pub fn allowed_methods(&self, path: &str) -> Vec<&'static str> {
103 let mut methods = Vec::new();
104 if self.get.at(path).is_some() {
105 methods.push("GET");
106 methods.push("HEAD");
107 }
108 if self.post.at(path).is_some() {
109 methods.push("POST");
110 }
111 if self.put.at(path).is_some() {
112 methods.push("PUT");
113 }
114 if self.patch.at(path).is_some() {
115 methods.push("PATCH");
116 }
117 if self.delete.at(path).is_some() {
118 methods.push("DELETE");
119 }
120 if self.head.at(path).is_some() && !methods.contains(&"HEAD") {
121 methods.push("HEAD");
122 }
123 if self.options.at(path).is_some() {
124 methods.push("OPTIONS");
125 }
126 methods
127 }
128}
129
130#[async_trait::async_trait]
131impl IRouter for Router {
132 fn register(&mut self, method: HttpMethod, path: &str, endpoint: Arc<dyn IEndpoint>) {
133 self.method_router_mut(method).insert(path, endpoint);
134 }
135
136 async fn match_route(
137 &self,
138 ctx: &mut dyn IHttpContext,
139 ) -> Result<Option<(Arc<dyn IEndpoint>, HashMap<String, String>, String)>> {
140 let path = ctx.request().path();
141 let method_str = ctx.request().method();
142 let method = match HttpMethod::from_str(method_str) {
143 Some(m) => m,
144 None => return Ok(None),
145 };
146
147 if let Some(matched) = self.method_router(method).at(path) {
148 return Ok(Some(collect_match(matched)));
149 }
150
151 if method == HttpMethod::Head {
157 if let Some(matched) = self.get.at(path) {
158 return Ok(Some(collect_match(matched)));
159 }
160 }
161
162 Ok(None)
163 }
164}
165
166fn collect_match(
167 matched: matchit::Match<'_, '_, &RouteValue>,
168) -> (Arc<dyn IEndpoint>, HashMap<String, String>, String) {
169 let mut params = HashMap::new();
170 for (key, value) in matched.params.iter() {
171 params.insert(key.to_string(), value.to_string());
172 }
173 let (endpoint, pattern) = matched.value;
174 (Arc::clone(endpoint), params, pattern.clone())
175}
176
177impl Default for Router {
178 fn default() -> Self {
179 Self::new()
180 }
181}