1use std::future::Future;
22use std::pin::Pin;
23use std::sync::Arc;
24use urlpattern::{UrlPattern, UrlPatternInit, UrlPatternMatchInput};
25use worker::*;
26
27pub struct Pattern(urlpattern::UrlPattern);
29
30pub fn path(v: &str) -> Result<Pattern> {
37 let init = UrlPatternInit {
38 pathname: Some(v.to_owned()),
39 ..Default::default()
40 };
41
42 let pattern = <UrlPattern>::parse(init, Default::default())
43 .map_err(|err| Error::RustError(format!("failed to parse route pattern: {err}")))?;
44 Ok(Pattern(pattern))
45}
46
47type Handler<State> = Box<dyn Fn(Request, Arc<State>) -> ResponseFuture + 'static>;
48pub type ResponseFuture = Pin<Box<dyn Future<Output = Result<Response>> + 'static>>;
49
50struct Route<State> {
51 pattern: Pattern,
52 handler: Handler<State>,
53 method: Method,
54}
55
56pub struct Router<State> {
58 state: Arc<State>,
59 routes: Vec<Route<State>>,
60}
61
62macro_rules! insert_method {
63 ($name:ident, $method:expr) => {
64 pub fn $name<HandlerFn, Res>(self, pattern: Pattern, handler: HandlerFn) -> Self
71 where
72 HandlerFn: Fn(Request, Arc<State>) -> Res + 'static,
73 Res: Future<Output = Result<Response>> + 'static,
74 {
75 self.insert($method, pattern, handler)
76 }
77 };
78}
79
80impl<State> Router<State> {
81 pub fn new_with_state(state: Arc<State>) -> Self {
84 Router {
85 routes: vec![],
86 state,
87 }
88 }
89
90 fn insert<HandlerFn, Res>(
91 mut self,
92 method: Method,
93 pattern: Pattern,
94 handler: HandlerFn,
95 ) -> Self
96 where
97 HandlerFn: Fn(Request, Arc<State>) -> Res + 'static,
98 Res: Future<Output = Result<Response>> + 'static,
99 {
100 self.routes.push(Route {
101 method,
102 pattern,
103 handler: Box::new(move |req, state| Box::pin(handler(req, state))),
104 });
105 self
106 }
107
108 insert_method!(head, Method::Head);
109 insert_method!(get, Method::Get);
110 insert_method!(post, Method::Post);
111 insert_method!(put, Method::Put);
112 insert_method!(patch, Method::Patch);
113 insert_method!(delete, Method::Delete);
114 insert_method!(options, Method::Options);
115 insert_method!(connect, Method::Connect);
116 insert_method!(trace, Method::Trace);
117
118 pub async fn run(&self, req: Request) -> Result<Response> {
119 let url = req.url()?;
120
121 for route in &self.routes {
122 if route.method != req.method() {
123 continue;
124 }
125
126 if let Some(_res) = route
127 .pattern
128 .0
129 .exec(UrlPatternMatchInput::Url(url.clone()))
130 .unwrap()
131 {
132 return (route.handler)(req, Arc::clone(&self.state)).await;
133 }
134 }
135
136 ResponseBuilder::new().error("page not found", 404)
137 }
138}