1use std::sync::Arc;
4
5use futures_util::future::BoxFuture;
6
7use crate::errors::CrawlError;
8use crate::handler::{Middleware, RequestHandler};
9use crate::request::{Method, Request};
10
11pub trait HasRequest {
13 fn request(&self) -> &Request;
15}
16
17#[derive(Debug, Clone)]
19#[non_exhaustive]
20pub enum MethodFilter {
21 Any,
23 Only(Vec<Method>),
25}
26
27impl MethodFilter {
28 pub fn matches(&self, method: &Method) -> bool {
30 match self {
31 Self::Any => true,
32 Self::Only(methods) => methods.contains(method),
33 }
34 }
35
36 fn shadows(&self, other: &Self) -> bool {
37 match (self, other) {
38 (Self::Any, _) => true,
39 (Self::Only(_), Self::Any) => false,
40 (Self::Only(earlier), Self::Only(later)) => {
41 later.iter().all(|method| earlier.contains(method))
42 }
43 }
44 }
45}
46
47pub struct Router<C> {
49 routes: Vec<Route<C>>,
50 default: Option<Arc<dyn RequestHandler<C>>>,
51 middleware: Vec<Arc<dyn Middleware<C>>>,
52}
53
54struct Route<C> {
56 label: Option<String>,
57 methods: MethodFilter,
58 handler: Arc<dyn RequestHandler<C>>,
59}
60
61impl<C: HasRequest + Send + 'static> Router<C> {
62 #[allow(clippy::new_without_default)]
66 pub fn new() -> Self {
67 Self {
68 routes: Vec::new(),
69 default: None,
70 middleware: Vec::new(),
71 }
72 }
73
74 pub fn route<H: RequestHandler<C>>(self, label: impl Into<String>, handler: H) -> Self {
76 self.push_route(Some(label.into()), MethodFilter::Any, handler)
77 }
78
79 pub fn route_method<H: RequestHandler<C>>(
81 self,
82 label: impl Into<String>,
83 method: Method,
84 handler: H,
85 ) -> Self {
86 self.route_methods(label, [method], handler)
87 }
88
89 pub fn route_methods<H, I>(self, label: impl Into<String>, methods: I, handler: H) -> Self
91 where
92 H: RequestHandler<C>,
93 I: IntoIterator<Item = Method>,
94 {
95 self.push_route(
96 Some(label.into()),
97 MethodFilter::Only(methods.into_iter().collect()),
98 handler,
99 )
100 }
101
102 pub fn default<H: RequestHandler<C>>(mut self, handler: H) -> Self {
104 self.default = Some(Arc::new(handler));
105 self
106 }
107
108 pub fn middleware<M: Middleware<C>>(mut self, middleware: M) -> Self {
110 self.middleware.push(Arc::new(middleware));
111 self
112 }
113
114 fn push_route<H: RequestHandler<C>>(
115 mut self,
116 label: Option<String>,
117 methods: MethodFilter,
118 handler: H,
119 ) -> Self {
120 if self.routes.iter().any(|route| {
121 (route.label.is_none() || route.label == label) && route.methods.shadows(&methods)
122 }) {
123 tracing::warn!(label = ?label, "route is unreachable because an earlier route shadows it");
124 }
125 self.routes.push(Route {
126 label,
127 methods,
128 handler: Arc::new(handler),
129 });
130 self
131 }
132}
133
134impl<C: HasRequest + Send + 'static> RequestHandler<C> for Router<C> {
135 fn handle(&self, ctx: C) -> BoxFuture<'static, Result<(), CrawlError>> {
136 let request = ctx.request();
137 let handler = self
138 .routes
139 .iter()
140 .find(|route| {
141 (route.label.is_none() || route.label.as_deref() == request.label.as_deref())
142 && route.methods.matches(&request.method)
143 })
144 .map(|route| Arc::clone(&route.handler))
145 .or_else(|| self.default.as_ref().map(Arc::clone));
146
147 let Some(handler) = handler else {
148 let error = CrawlError::MissingRoute {
149 label: request.label.clone(),
150 method: request.method.clone(),
151 };
152 return Box::pin(async move { Err(error) });
153 };
154 let middleware = self.middleware.clone();
155
156 Box::pin(async move {
157 let mut ctx = ctx;
158 for middleware in middleware {
159 ctx = middleware.run(ctx).await?;
160 }
161 handler.handle(ctx).await
162 })
163 }
164}