1use std::sync::Arc;
4
5use serve_net::{Method, WorkClass};
6
7use crate::{Cx, Result};
8
9pub trait Handler<I>: Send + Sync + 'static {
11 fn call(&self, cx: &mut Cx<'_, I>) -> Result;
18}
19
20impl<I, F> Handler<I> for F
21where
22 F: Fn(&mut Cx<'_, I>) -> Result + Send + Sync + 'static,
23{
24 #[inline]
25 fn call(&self, cx: &mut Cx<'_, I>) -> Result {
26 self(cx)
27 }
28}
29
30#[derive(Debug)]
32pub(crate) enum Access<R> {
33 Undeclared,
35 Public,
37 Rule(Arc<R>),
39}
40
41impl<R> Clone for Access<R> {
42 fn clone(&self) -> Self {
43 match self {
44 Self::Undeclared => Self::Undeclared,
45 Self::Public => Self::Public,
46 Self::Rule(r) => Self::Rule(Arc::clone(r)),
47 }
48 }
49}
50
51pub(crate) type BoxHandler<I> = Arc<dyn Handler<I>>;
52
53pub struct Route<I, R> {
100 pub(crate) pattern: String,
101 pub(crate) handlers: Vec<(Method, BoxHandler<I>)>,
102 pub(crate) access: Access<R>,
103 pub(crate) name: Option<&'static str>,
104 pub(crate) work: WorkClass,
105 pub(crate) template: Option<&'static str>,
106 pub(crate) csrf_exempt: Option<&'static str>,
107 pub(crate) form_limits: form_net::Limits,
108 pub(crate) cors: Option<Arc<crate::Cors>>,
109 pub(crate) cache: crate::CachePolicy,
110}
111
112impl<I: 'static, R> Route<I, R> {
113 #[must_use]
115 pub fn at(pattern: impl Into<String>) -> Self {
116 Self {
117 pattern: pattern.into(),
118 handlers: Vec::new(),
119 access: Access::Undeclared,
120 name: None,
121 work: WorkClass::Quick,
122 template: None,
123 csrf_exempt: None,
124 form_limits: form_net::Limits::DEFAULT,
125 cors: None,
126 cache: crate::CachePolicy::Unset,
127 }
128 }
129
130 #[must_use]
133 pub fn on(mut self, method: Method, handler: impl Handler<I>) -> Self {
134 self.handlers.push((method, Arc::new(handler)));
135 self
136 }
137
138 #[must_use]
140 pub fn get<F>(self, f: F) -> Self
141 where
142 F: Fn(&mut Cx<'_, I>) -> Result + Send + Sync + 'static,
143 {
144 self.on(Method::Get, f)
145 }
146
147 #[must_use]
149 pub fn post<F>(self, f: F) -> Self
150 where
151 F: Fn(&mut Cx<'_, I>) -> Result + Send + Sync + 'static,
152 {
153 self.on(Method::Post, f)
154 }
155
156 #[must_use]
158 pub fn put<F>(self, f: F) -> Self
159 where
160 F: Fn(&mut Cx<'_, I>) -> Result + Send + Sync + 'static,
161 {
162 self.on(Method::Put, f)
163 }
164
165 #[must_use]
167 pub fn patch<F>(self, f: F) -> Self
168 where
169 F: Fn(&mut Cx<'_, I>) -> Result + Send + Sync + 'static,
170 {
171 self.on(Method::Patch, f)
172 }
173
174 #[must_use]
176 pub fn delete<F>(self, f: F) -> Self
177 where
178 F: Fn(&mut Cx<'_, I>) -> Result + Send + Sync + 'static,
179 {
180 self.on(Method::Delete, f)
181 }
182
183 #[must_use]
187 pub fn public(mut self) -> Self {
188 self.access = Access::Public;
189 self
190 }
191
192 #[must_use]
195 pub fn allow(mut self, rule: R) -> Self {
196 self.access = Access::Rule(Arc::new(rule));
197 self
198 }
199
200 #[must_use]
202 pub fn name(mut self, name: &'static str) -> Self {
203 self.name = Some(name);
204 self
205 }
206
207 #[must_use]
212 pub fn work(mut self, work: WorkClass) -> Self {
213 self.work = work;
214 self
215 }
216
217 #[must_use]
222 pub fn csrf_exempt(mut self, reason: &'static str) -> Self {
223 self.csrf_exempt = Some(reason);
224 self
225 }
226
227 #[must_use]
231 pub fn form_limits(mut self, limits: form_net::Limits) -> Self {
232 self.form_limits = limits;
233 self
234 }
235
236 #[must_use]
239 pub fn cors(mut self, cors: crate::Cors) -> Self {
240 self.cors = Some(Arc::new(cors));
241 self
242 }
243
244 #[must_use]
246 pub fn cache(mut self, policy: crate::CachePolicy) -> Self {
247 self.cache = policy;
248 self
249 }
250
251 #[must_use]
254 pub fn template(mut self, name: &'static str) -> Self {
255 self.template = Some(name);
256 self
257 }
258}
259
260impl<I, R> std::fmt::Debug for Route<I, R> {
261 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
262 f.debug_struct("Route")
263 .field("pattern", &self.pattern)
264 .field(
265 "methods",
266 &self.handlers.iter().map(|(m, _)| *m).collect::<Vec<_>>(),
267 )
268 .field("name", &self.name)
269 .finish_non_exhaustive()
270 }
271}
272
273macro_rules! method_fn {
274 ($(#[$doc:meta])* $fn:ident, $m:ident) => {
275 $(#[$doc])*
276 #[must_use]
277 pub fn $fn<I: 'static, R, F>(pattern: impl Into<String>, f: F) -> Route<I, R>
278 where
279 F: Fn(&mut Cx<'_, I>) -> Result + Send + Sync + 'static,
280 {
281 Route::at(pattern).on(Method::$m, f)
282 }
283 };
284}
285
286method_fn!(
287 get, Get
289);
290method_fn!(
291 post, Post
293);
294method_fn!(
295 put, Put
297);
298method_fn!(
299 patch, Patch
301);
302method_fn!(
303 delete, Delete
305);
306
307pub struct Group<I, R> {
326 pub(crate) prefix: String,
327 pub(crate) access: Access<R>,
328 pub(crate) routes: Vec<Route<I, R>>,
329 pub(crate) cors: Option<Arc<crate::Cors>>,
330 pub(crate) cache: crate::CachePolicy,
331}
332
333impl<I: 'static, R> Group<I, R> {
334 #[must_use]
336 pub fn new(prefix: impl Into<String>) -> Self {
337 Self {
338 prefix: prefix.into(),
339 access: Access::Undeclared,
340 routes: Vec::new(),
341 cors: None,
342 cache: crate::CachePolicy::Unset,
343 }
344 }
345
346 #[must_use]
348 pub fn cors(mut self, cors: crate::Cors) -> Self {
349 self.cors = Some(Arc::new(cors));
350 self
351 }
352
353 #[must_use]
355 pub fn cache(mut self, policy: crate::CachePolicy) -> Self {
356 self.cache = policy;
357 self
358 }
359
360 #[must_use]
362 pub fn public(mut self) -> Self {
363 self.access = Access::Public;
364 self
365 }
366
367 #[must_use]
369 pub fn allow(mut self, rule: R) -> Self {
370 self.access = Access::Rule(Arc::new(rule));
371 self
372 }
373
374 #[must_use]
376 pub fn route(mut self, route: Route<I, R>) -> Self {
377 self.routes.push(route);
378 self
379 }
380
381 #[must_use]
384 pub fn group(mut self, group: Group<I, R>) -> Self {
385 let inherited = match &group.access {
386 Access::Undeclared => self.access.clone(),
387 a => a.clone(),
388 };
389 let cors = group.cors.or_else(|| self.cors.clone());
390 let cache = if group.cache == crate::CachePolicy::Unset {
391 self.cache
392 } else {
393 group.cache
394 };
395 for mut r in group.routes {
396 r.pattern = join(&group.prefix, &r.pattern);
397 if matches!(r.access, Access::Undeclared) {
398 r.access = inherited.clone();
399 }
400 if r.cors.is_none() {
401 r.cors.clone_from(&cors);
402 }
403 if r.cache == crate::CachePolicy::Unset {
404 r.cache = cache;
405 }
406 self.routes.push(r);
407 }
408 self
409 }
410
411 pub(crate) fn into_routes(self) -> impl Iterator<Item = Route<I, R>> {
412 let prefix = self.prefix;
413 let access = self.access;
414 let cors = self.cors;
415 let cache = self.cache;
416 self.routes.into_iter().map(move |mut r| {
417 r.pattern = join(&prefix, &r.pattern);
418 if matches!(r.access, Access::Undeclared) {
419 r.access = access.clone();
420 }
421 if r.cors.is_none() {
422 r.cors.clone_from(&cors);
423 }
424 if r.cache == crate::CachePolicy::Unset {
425 r.cache = cache;
426 }
427 r
428 })
429 }
430}
431
432impl<I, R> std::fmt::Debug for Group<I, R> {
433 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
434 f.debug_struct("Group")
435 .field("prefix", &self.prefix)
436 .field("routes", &self.routes)
437 .finish_non_exhaustive()
438 }
439}
440
441pub(crate) fn join(prefix: &str, pattern: &str) -> String {
443 let p = prefix.trim_end_matches('/');
444 if pattern == "/" || pattern.is_empty() {
445 if p.is_empty() { "/".into() } else { p.into() }
446 } else if pattern.starts_with('/') {
447 format!("{p}{pattern}")
448 } else {
449 format!("{p}/{pattern}")
450 }
451}
452
453#[cfg(test)]
454mod tests {
455 use super::join;
456
457 #[test]
458 fn test_join() {
459 assert_eq!(join("/admin", "/"), "/admin");
460 assert_eq!(join("/admin/", "/users"), "/admin/users");
461 assert_eq!(join("/", "/x"), "/x");
462 assert_eq!(join("", "/"), "/");
463 assert_eq!(join("/a", "b"), "/a/b");
464 }
465}