gotham/router/builder/
draw.rs

1use std::marker::PhantomData;
2use std::panic::RefUnwindSafe;
3
4use hyper::Method;
5use log::trace;
6
7use crate::extractor::{NoopPathExtractor, NoopQueryStringExtractor};
8use crate::helpers::http::request::path::split_path_segments;
9use crate::pipeline::{PipelineHandleChain, PipelineSet};
10use crate::router::builder::{
11    AssociatedRouteBuilder, DelegateRouteBuilder, RouterBuilder, ScopeBuilder, SingleRouteBuilder,
12};
13use crate::router::route::matcher::{
14    AnyRouteMatcher, IntoRouteMatcher, MethodOnlyRouteMatcher, RouteMatcher,
15};
16use crate::router::tree::node::Node;
17use crate::router::tree::regex::ConstrainedSegmentRegex;
18use crate::router::tree::segment::SegmentType;
19
20/// The type returned when building a route that only considers path and http verb(s) when
21/// determining if it matches a request.
22///
23/// See `router::builder::DefineSingleRoute` for an overview of route specification.
24pub(crate) type DefaultSingleRouteBuilder<'a, C, P> = SingleRouteBuilder<
25    'a,
26    MethodOnlyRouteMatcher,
27    C,
28    P,
29    NoopPathExtractor,
30    NoopQueryStringExtractor,
31>;
32
33/// The type returned when building a route with explicit matching requirements.
34///
35/// See `router::builder::DefineSingleRoute` for an overview of route specification.
36pub(crate) type ExplicitSingleRouteBuilder<'a, M, C, P> =
37    SingleRouteBuilder<'a, M, C, P, NoopPathExtractor, NoopQueryStringExtractor>;
38
39/// The type passed to the function used when building associated routes. See
40/// `AssociatedRouteBuilder` for information about the API available for associated routes.
41pub(crate) type DefaultAssociatedRouteBuilder<'a, M, C, P> =
42    AssociatedRouteBuilder<'a, M, C, P, NoopPathExtractor, NoopQueryStringExtractor>;
43
44/// Defines functions used by a builder to determine which request paths will be dispatched to a
45/// route. This trait is implemented by the top-level `RouterBuilder`, and also the `ScopedBuilder`
46/// created by `DrawRoutes::scope`.
47pub trait DrawRoutes<C, P>
48where
49    C: PipelineHandleChain<P> + Copy + Send + Sync + 'static,
50    P: RefUnwindSafe + Send + Sync + 'static,
51{
52    /// Creates a route which matches `GET` and `HEAD` requests to the given path.
53    ///
54    /// # Examples
55    ///
56    /// ```rust
57    /// # use hyper::{Body, Response, StatusCode};
58    /// # use gotham::state::State;
59    /// # use gotham::router::Router;
60    /// # use gotham::router::builder::*;
61    /// # use gotham::test::TestServer;
62    /// #
63    /// # fn my_handler(state: State) -> (State, Response<Body>) {
64    /// #   (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
65    /// # }
66    /// #
67    /// # fn router() -> Router {
68    /// build_simple_router(|route| {
69    ///     route.get_or_head("/request/path").to(my_handler);
70    /// })
71    /// # }
72    /// #
73    /// # fn main() {
74    /// #   let test_server = TestServer::new(router()).unwrap();
75    /// #
76    /// #   let response = test_server.client()
77    /// #       .get("https://example.com/request/path")
78    /// #       .perform()
79    /// #       .unwrap();
80    /// #   assert_eq!(response.status(), StatusCode::ACCEPTED);
81    /// #
82    /// #   let response = test_server.client()
83    /// #       .head("https://example.com/request/path")
84    /// #       .perform()
85    /// #       .unwrap();
86    /// #   assert_eq!(response.status(), StatusCode::ACCEPTED);
87    /// # }
88    /// ```
89    fn get_or_head<'b>(&'b mut self, path: &str) -> DefaultSingleRouteBuilder<'b, C, P> {
90        self.request(vec![Method::GET, Method::HEAD], path)
91    }
92
93    /// Creates a route which matches **only** `GET` requests to the given path (ignoring `HEAD`
94    /// requests).
95    ///
96    /// # Examples
97    ///
98    /// ```rust
99    /// # extern crate gotham;
100    /// # extern crate hyper;
101    /// #
102    /// # use hyper::{Body, Response, StatusCode};
103    /// # use gotham::state::State;
104    /// # use gotham::router::Router;
105    /// # use gotham::router::builder::*;
106    /// # use gotham::test::TestServer;
107    /// #
108    /// # fn my_handler(state: State) -> (State, Response<Body>) {
109    /// #   (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
110    /// # }
111    /// #
112    /// # fn router() -> Router {
113    /// build_simple_router(|route| {
114    ///     route.get("/request/path").to(my_handler);
115    /// })
116    /// # }
117    /// #
118    /// # fn main() {
119    /// #   let test_server = TestServer::new(router()).unwrap();
120    /// #   let response = test_server.client()
121    /// #       .get("https://example.com/request/path")
122    /// #       .perform()
123    /// #       .unwrap();
124    /// #   assert_eq!(response.status(), StatusCode::ACCEPTED);
125    /// # }
126    /// ```
127    fn get<'b>(&'b mut self, path: &str) -> DefaultSingleRouteBuilder<'b, C, P> {
128        self.request(vec![Method::GET], path)
129    }
130
131    /// Creates a route which matches `HEAD` requests to the given path.
132    ///
133    /// # Examples
134    ///
135    /// ```rust
136    /// # extern crate gotham;
137    /// # extern crate hyper;
138    /// #
139    /// # use hyper::{Body, Response, StatusCode};
140    /// # use gotham::state::State;
141    /// # use gotham::router::Router;
142    /// # use gotham::router::builder::*;
143    /// # use gotham::test::TestServer;
144    /// #
145    /// # fn my_handler(state: State) -> (State, Response<Body>) {
146    /// #   (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
147    /// # }
148    /// #
149    /// # fn router() -> Router {
150    /// build_simple_router(|route| {
151    ///     route.head("/request/path").to(my_handler);
152    /// })
153    /// # }
154    /// #
155    /// # fn main() {
156    /// #   let test_server = TestServer::new(router()).unwrap();
157    /// #   let response = test_server.client()
158    /// #       .head("https://example.com/request/path")
159    /// #       .perform()
160    /// #       .unwrap();
161    /// #   assert_eq!(response.status(), StatusCode::ACCEPTED);
162    /// # }
163    /// ```
164    fn head<'b>(&'b mut self, path: &str) -> DefaultSingleRouteBuilder<'b, C, P> {
165        self.request(vec![Method::HEAD], path)
166    }
167
168    /// Creates a route which matches `POST` requests to the given path.
169    ///
170    /// # Examples
171    ///
172    /// ```rust
173    /// # extern crate gotham;
174    /// # extern crate hyper;
175    /// # extern crate mime;
176    /// #
177    /// # use hyper::{Body, Response, StatusCode};
178    /// # use gotham::state::State;
179    /// # use gotham::router::Router;
180    /// # use gotham::router::builder::*;
181    /// # use gotham::test::TestServer;
182    /// #
183    /// # fn my_handler(state: State) -> (State, Response<Body>) {
184    /// #   (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
185    /// # }
186    /// #
187    /// # fn router() -> Router {
188    /// build_simple_router(|route| {
189    ///     route.post("/request/path").to(my_handler);
190    /// })
191    /// # }
192    /// #
193    /// # fn main() {
194    /// #   let test_server = TestServer::new(router()).unwrap();
195    /// #   let response = test_server.client()
196    /// #       .post("https://example.com/request/path", b"".to_vec(), mime::TEXT_PLAIN)
197    /// #       .perform()
198    /// #       .unwrap();
199    /// #   assert_eq!(response.status(), StatusCode::ACCEPTED);
200    /// # }
201    /// ```
202    fn post<'b>(&'b mut self, path: &str) -> DefaultSingleRouteBuilder<'b, C, P> {
203        self.request(vec![Method::POST], path)
204    }
205
206    /// Creates a route which matches `PUT` requests to the given path.
207    ///
208    /// # Examples
209    ///
210    /// ```rust
211    /// # extern crate gotham;
212    /// # extern crate hyper;
213    /// # extern crate mime;
214    /// #
215    /// # use hyper::{Body, Response, StatusCode};
216    /// # use gotham::state::State;
217    /// # use gotham::router::Router;
218    /// # use gotham::router::builder::*;
219    /// # use gotham::test::TestServer;
220    /// #
221    /// # fn my_handler(state: State) -> (State, Response<Body>) {
222    /// #   (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
223    /// # }
224    /// #
225    /// # fn router() -> Router {
226    /// build_simple_router(|route| {
227    ///     route.put("/request/path").to(my_handler);
228    /// })
229    /// # }
230    /// #
231    /// # fn main() {
232    /// #   let test_server = TestServer::new(router()).unwrap();
233    /// #   let response = test_server.client()
234    /// #       .put("https://example.com/request/path", b"".to_vec(), mime::TEXT_PLAIN)
235    /// #       .perform()
236    /// #       .unwrap();
237    /// #   assert_eq!(response.status(), StatusCode::ACCEPTED);
238    /// # }
239    /// ```
240    fn put<'b>(&'b mut self, path: &str) -> DefaultSingleRouteBuilder<'b, C, P> {
241        self.request(vec![Method::PUT], path)
242    }
243
244    /// Creates a route which matches `PATCH` requests to the given path.
245    ///
246    /// # Examples
247    ///
248    /// ```rust
249    /// # extern crate gotham;
250    /// # extern crate hyper;
251    /// # extern crate mime;
252    /// #
253    /// # use hyper::{Body, Response, StatusCode};
254    /// # use gotham::state::State;
255    /// # use gotham::router::Router;
256    /// # use gotham::router::builder::*;
257    /// # use gotham::test::TestServer;
258    /// #
259    /// # fn my_handler(state: State) -> (State, Response<Body>) {
260    /// #   (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
261    /// # }
262    /// #
263    /// # fn router() -> Router {
264    /// build_simple_router(|route| {
265    ///     route.patch("/request/path").to(my_handler);
266    /// })
267    /// # }
268    /// #
269    /// # fn main() {
270    /// #   let test_server = TestServer::new(router()).unwrap();
271    /// #   let response = test_server.client()
272    /// #       .patch("https://example.com/request/path", b"".to_vec(), mime::TEXT_PLAIN)
273    /// #       .perform()
274    /// #       .unwrap();
275    /// #   assert_eq!(response.status(), StatusCode::ACCEPTED);
276    /// # }
277    /// ```
278    fn patch<'b>(&'b mut self, path: &str) -> DefaultSingleRouteBuilder<'b, C, P> {
279        self.request(vec![Method::PATCH], path)
280    }
281
282    /// Creates a route which matches `DELETE` requests to the given path.
283    ///
284    /// # Examples
285    ///
286    /// ```rust
287    /// # extern crate gotham;
288    /// # extern crate hyper;
289    /// #
290    /// # use hyper::{Body, Response, StatusCode};
291    /// # use gotham::state::State;
292    /// # use gotham::router::Router;
293    /// # use gotham::router::builder::*;
294    /// # use gotham::test::TestServer;
295    /// #
296    /// # fn my_handler(state: State) -> (State, Response<Body>) {
297    /// #   (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
298    /// # }
299    /// #
300    /// # fn router() -> Router {
301    /// build_simple_router(|route| {
302    ///     route.delete("/request/path").to(my_handler);
303    /// })
304    /// # }
305    /// #
306    /// # fn main() {
307    /// #   let test_server = TestServer::new(router()).unwrap();
308    /// #   let response = test_server.client()
309    /// #       .delete("https://example.com/request/path")
310    /// #       .perform()
311    /// #       .unwrap();
312    /// #   assert_eq!(response.status(), StatusCode::ACCEPTED);
313    /// # }
314    /// ```
315    fn delete<'b>(&'b mut self, path: &str) -> DefaultSingleRouteBuilder<'b, C, P> {
316        self.request(vec![Method::DELETE], path)
317    }
318
319    /// Creates a route which matches `OPTIONS` requests to the given path.
320    ///
321    /// # Examples
322    ///
323    /// ```rust
324    /// # extern crate gotham;
325    /// # extern crate hyper;
326    /// #
327    /// # use hyper::{Body, Response, StatusCode};
328    /// # use gotham::state::State;
329    /// # use gotham::router::Router;
330    /// # use gotham::router::builder::*;
331    /// # use gotham::test::TestServer;
332    /// #
333    /// # fn my_handler(state: State) -> (State, Response<Body>) {
334    /// #   (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
335    /// # }
336    /// #
337    /// # fn router() -> Router {
338    /// build_simple_router(|route| {
339    ///     route.options("/request/path").to(my_handler);
340    /// })
341    /// # }
342    /// #
343    /// # fn main() {
344    /// #   let test_server = TestServer::new(router()).unwrap();
345    /// #   let response = test_server.client()
346    /// #       .options("https://example.com/request/path")
347    /// #       .perform()
348    /// #       .unwrap();
349    /// #   assert_eq!(response.status(), StatusCode::ACCEPTED);
350    /// # }
351    /// ```
352    fn options<'b>(&'b mut self, path: &str) -> DefaultSingleRouteBuilder<'b, C, P> {
353        self.request(vec![Method::OPTIONS], path)
354    }
355
356    /// Creates a single route which matches any requests to the given `path` with one of the
357    /// given `methods`. The `path` can consist of static or dynamic segments, for example:
358    ///
359    /// * `"/hello/world"` - a static path, matching only a request for exactly `"/hello/world"`
360    /// * `"/hello/:name"` - a dynamic path, matching requests for `"/hello/any_value_here"`
361    ///
362    /// # Examples
363    ///
364    /// ```rust
365    /// # extern crate gotham;
366    /// # extern crate hyper;
367    /// #
368    /// # use hyper::{Body, Response, StatusCode};
369    /// # use hyper::Method;
370    /// # use gotham::state::State;
371    /// # use gotham::router::Router;
372    /// # use gotham::router::builder::*;
373    /// # use gotham::test::TestServer;
374    /// #
375    /// # fn my_handler(state: State) -> (State, Response<Body>) {
376    /// #   (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
377    /// # }
378    /// #
379    /// # fn router() -> Router {
380    /// build_simple_router(|route| {
381    ///     route.request(vec![Method::GET, Method::HEAD], "/request/path").to(my_handler);
382    /// })
383    /// # }
384    /// #
385    /// # fn main() {
386    /// #   let test_server = TestServer::new(router()).unwrap();
387    /// #
388    /// #   let response = test_server.client()
389    /// #       .get("https://example.com/request/path")
390    /// #       .perform()
391    /// #       .unwrap();
392    /// #   assert_eq!(response.status(), StatusCode::ACCEPTED);
393    /// #
394    /// #   let response = test_server.client()
395    /// #       .head("https://example.com/request/path")
396    /// #       .perform()
397    /// #       .unwrap();
398    /// #   assert_eq!(response.status(), StatusCode::ACCEPTED);
399    /// # }
400    /// ```
401    ///
402    /// ```
403    /// # extern crate gotham;
404    /// # extern crate hyper;
405    /// # extern crate mime;
406    /// #
407    /// # use hyper::{Body, Response, StatusCode};
408    /// # use hyper::header::ACCEPT;
409    /// # use gotham::state::State;
410    /// # use gotham::router::route::matcher::AcceptHeaderRouteMatcher;
411    /// # use gotham::router::Router;
412    /// # use gotham::router::builder::*;
413    /// # use gotham::test::TestServer;
414    /// #
415    /// # fn my_handler(state: State) -> (State, Response<Body>) {
416    /// #   (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
417    /// # }
418    /// #
419    /// # fn router() -> Router {
420    /// build_simple_router(|route| {
421    ///     // All we match on is the Accept header, the method is not considered.
422    ///     let matcher = AcceptHeaderRouteMatcher::new(vec![mime::APPLICATION_JSON]);
423    ///     route.request(matcher, "/request/path").to(my_handler);
424    /// })
425    /// # }
426    /// #
427    /// # fn main() {
428    /// #   let test_server = TestServer::new(router()).unwrap();
429    /// #
430    /// #   let response = test_server.client()
431    /// #       .get("https://example.com/request/path")
432    /// #       .with_header(ACCEPT, mime::APPLICATION_JSON.to_string().parse().unwrap())
433    /// #       .perform()
434    /// #       .unwrap();
435    /// #   assert_eq!(response.status(), StatusCode::ACCEPTED);
436    /// #
437    /// #   let response = test_server.client()
438    /// #       .get("https://example.com/request/path")
439    /// #       .with_header(ACCEPT, mime::TEXT_PLAIN.to_string().parse().unwrap())
440    /// #       .perform()
441    /// #       .unwrap();
442    /// #   assert_eq!(response.status(), StatusCode::NOT_ACCEPTABLE);
443    /// #
444    /// #   // No Accept type being provided is valid for the AcceptHeaderRouterMatcher
445    /// #   // Proves the method is not considered
446    /// #   let response = test_server.client()
447    /// #       .delete("https://example.com/request/path")
448    /// #       .perform()
449    /// #       .unwrap();
450    /// #   assert_eq!(response.status(), StatusCode::ACCEPTED);
451    /// # }
452    /// ```
453    fn request<'b, IRM, M>(
454        &'b mut self,
455        matcher: IRM,
456        path: &str,
457    ) -> ExplicitSingleRouteBuilder<'b, M, C, P>
458    where
459        IRM: IntoRouteMatcher<Output = M>,
460        M: RouteMatcher + Send + Sync + 'static,
461    {
462        let (node_builder, pipeline_chain, pipelines) = self.component_refs();
463        let node_builder = descend(node_builder, path);
464        let matcher = matcher.into_route_matcher();
465
466        SingleRouteBuilder {
467            matcher,
468            node_builder,
469            pipeline_chain: *pipeline_chain,
470            pipelines: pipelines.clone(),
471            phantom: PhantomData,
472        }
473    }
474
475    /// Begins defining a new scope, based on a given `path` prefix.
476    ///
477    /// # Examples
478    ///
479    /// ```rust
480    /// # extern crate gotham;
481    /// # extern crate hyper;
482    ///
483    /// # use hyper::{Body, Response, StatusCode};
484    /// # use gotham::state::State;
485    /// # use gotham::router::Router;
486    /// # use gotham::router::builder::*;
487    /// # use gotham::test::TestServer;
488    /// #
489    /// # mod api {
490    /// #   use super::*;
491    /// #   pub fn list(state: State) -> (State, Response<Body>) {
492    /// #       (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
493    /// #   }
494    /// # }
495    /// #
496    /// # fn router() -> Router {
497    /// build_simple_router(|route| {
498    ///     route.scope("/api", |route| {
499    ///         // Match requests to `/api/list`
500    ///         route.get("/list").to(api::list);
501    ///     });
502    /// })
503    /// # }
504    /// #
505    /// # fn main() {
506    /// #   let test_server = TestServer::new(router()).unwrap();
507    /// #   let response = test_server.client()
508    /// #       .get("https://example.com/api/list")
509    /// #       .perform()
510    /// #       .unwrap();
511    /// #   assert_eq!(response.status(), StatusCode::ACCEPTED);
512    /// # }
513    /// ```
514    fn scope<F>(&mut self, path: &str, f: F)
515    where
516        F: FnOnce(&mut ScopeBuilder<'_, C, P>),
517    {
518        let (node_builder, pipeline_chain, pipelines) = self.component_refs();
519        let node_builder = descend(node_builder, path);
520
521        let mut scope_builder = ScopeBuilder {
522            node_builder,
523            pipeline_chain: *pipeline_chain,
524            pipelines: pipelines.clone(),
525        };
526
527        f(&mut scope_builder)
528    }
529
530    /// Begins a new scope at the current location, with an alternate pipeline chain.
531    ///
532    /// # Examples
533    ///
534    /// ```rust
535    /// # use hyper::{Body, Response, StatusCode};
536    /// # use gotham::state::State;
537    /// # use gotham::middleware::session::{NewSessionMiddleware, SessionData};
538    /// # use gotham::router::Router;
539    /// # use gotham::router::builder::*;
540    /// # use gotham::pipeline::{finalize_pipeline_set, new_pipeline_set, new_pipeline};
541    /// # use gotham::test::TestServer;
542    /// # use serde::{Deserialize, Serialize};
543    /// #
544    /// # #[derive(Default, Serialize, Deserialize)]
545    /// # struct Session;
546    /// #
547    /// # #[derive(Default, Serialize, Deserialize)]
548    /// # struct AdminSession;
549    /// #
550    /// # mod resource {
551    /// #   use super::*;
552    /// #   pub fn list(state: State) -> (State, Response<Body>) {
553    /// #       assert!(state.has::<SessionData<Session>>());
554    /// #       assert!(!state.has::<SessionData<AdminSession>>());
555    /// #       (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
556    /// #   }
557    /// # }
558    /// #
559    /// # mod admin {
560    /// #   use super::*;
561    /// #   pub fn handler(state: State) -> (State, Response<Body>) {
562    /// #       assert!(state.has::<SessionData<Session>>());
563    /// #       assert!(state.has::<SessionData<AdminSession>>());
564    /// #       (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
565    /// #   }
566    /// # }
567    /// #
568    /// # fn handler(state: State) -> (State, Response<Body>) {
569    /// #   assert!(!state.has::<SessionData<Session>>());
570    /// #   assert!(!state.has::<SessionData<AdminSession>>());
571    /// #   (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
572    /// # }
573    /// #
574    /// # fn router() -> Router {
575    /// let pipelines = new_pipeline_set();
576    /// let (pipelines, default) = pipelines.add(
577    ///     new_pipeline()
578    ///         .add(NewSessionMiddleware::default().with_session_type::<Session>())
579    ///         .build()
580    /// );
581    /// let (pipelines, extended) = pipelines.add(
582    ///     new_pipeline()
583    ///         .add(NewSessionMiddleware::default().with_session_type::<AdminSession>())
584    ///         .build()
585    /// );
586    /// let pipeline_set = finalize_pipeline_set(pipelines);
587    ///
588    /// let default_chain = (default, ());
589    /// let extended_chain = (extended, default_chain);
590    ///
591    /// build_router(default_chain, pipeline_set, |route| {
592    ///     // Requests for the root handler use an empty set of pipelines, skipping the session
593    ///     // middlewares.
594    ///     route.with_pipeline_chain((), |route| {
595    ///         route.get("/").to(handler);
596    ///     });
597    ///
598    ///     // Requests dispatched to the resource module will only invoke one session
599    ///     // middleware which is the default behavior.
600    ///     route.get("/resource/list").to(resource::list);
601    ///
602    ///     // Requests for the admin handler will additionally invoke the admin session
603    ///     // middleware.
604    ///     route.with_pipeline_chain(extended_chain, |route| {
605    ///         route.get("/admin").to(admin::handler);
606    ///     });
607    /// })
608    /// # }
609    /// #
610    /// # fn main() {
611    /// #   let test_server = TestServer::new(router()).unwrap();
612    /// #
613    /// #   let response = test_server.client()
614    /// #       .get("https://example.com/")
615    /// #       .perform()
616    /// #       .unwrap();
617    /// #   assert_eq!(response.status(), StatusCode::ACCEPTED);
618    /// #
619    /// #   let response = test_server.client()
620    /// #       .get("https://example.com/resource/list")
621    /// #       .perform()
622    /// #       .unwrap();
623    /// #   assert_eq!(response.status(), StatusCode::ACCEPTED);
624    /// # }
625    /// ```
626    fn with_pipeline_chain<F, NC>(&mut self, pipeline_chain: NC, f: F)
627    where
628        F: FnOnce(&mut ScopeBuilder<'_, NC, P>),
629        NC: PipelineHandleChain<P> + Copy + Send + Sync + 'static,
630    {
631        let (node_builder, _pipeline_chain, pipelines) = self.component_refs();
632
633        let mut scope_builder = ScopeBuilder {
634            node_builder,
635            pipeline_chain,
636            pipelines: pipelines.clone(),
637        };
638
639        f(&mut scope_builder)
640    }
641
642    /// Begins delegating a subpath of the tree.
643    ///
644    /// # Examples
645    ///
646    /// ```rust
647    /// # extern crate gotham;
648    /// # extern crate hyper;
649    /// #
650    /// # use hyper::{Body, Response, StatusCode};
651    /// # use gotham::router::Router;
652    /// # use gotham::router::builder::*;
653    /// # use gotham::state::State;
654    /// # use gotham::test::TestServer;
655    /// #
656    /// fn admin_router() -> Router {
657    ///     // Implementation elided
658    /// #   fn handler(state: State) -> (State, Response<Body>) {
659    /// #       (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
660    /// #   }
661    /// #
662    /// #   build_simple_router(|route| {
663    /// #       route.get("/").to(handler);
664    /// #   })
665    /// }
666    ///
667    /// # fn router() -> Router {
668    /// build_simple_router(|route| {
669    ///     route.delegate("/admin").to_router(admin_router());
670    /// })
671    /// # }
672    /// #
673    /// # fn main() {
674    /// #   let test_server = TestServer::new(router()).unwrap();
675    /// #   let response = test_server.client()
676    /// #       .get("https://example.com/admin")
677    /// #       .perform()
678    /// #       .unwrap();
679    /// #   assert_eq!(response.status(), StatusCode::ACCEPTED);
680    /// # }
681    /// ```
682    fn delegate<'b>(&'b mut self, path: &str) -> DelegateRouteBuilder<'b, AnyRouteMatcher, C, P> {
683        let (node_builder, pipeline_chain, pipelines) = self.component_refs();
684        let node_builder = descend(node_builder, path);
685
686        DelegateRouteBuilder {
687            matcher: AnyRouteMatcher::new(),
688            node_builder,
689            pipeline_chain: *pipeline_chain,
690            pipelines: pipelines.clone(),
691        }
692    }
693
694    /// Begins delegating a subpath of the tree, but does not dispatch the requests via this
695    /// router's `PipelineChain`.
696    ///
697    /// # Examples
698    ///
699    /// ```rust
700    /// # use hyper::{Body, Response, StatusCode};
701    /// # use gotham::router::Router;
702    /// # use gotham::router::builder::*;
703    /// # use gotham::pipeline::{new_pipeline, single_pipeline};
704    /// # use gotham::state::State;
705    /// # use gotham::middleware::session::{NewSessionMiddleware, SessionData};
706    /// # use gotham::test::TestServer;
707    /// # use serde::{Deserialize, Serialize};
708    /// #
709    /// # #[derive(Default, Serialize, Deserialize)]
710    /// # struct Session;
711    /// #
712    /// // API routes which don't require sessions.
713    /// fn api_router() -> Router {
714    ///     // Implementation elided
715    /// #   fn handler(state: State) -> (State, Response<Body>) {
716    /// #       assert!(!state.has::<SessionData<Session>>());
717    /// #       (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
718    /// #   }
719    /// #
720    /// #   build_simple_router(|route| {
721    /// #       route.get("/").to(handler);
722    /// #   })
723    /// }
724    /// # fn handler(state: State) -> (State, Response<Body>) {
725    /// #   assert!(state.has::<SessionData<Session>>());
726    /// #   (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
727    /// # }
728    ///
729    /// # fn router() -> Router {
730    /// let (chain, pipelines) = single_pipeline(
731    ///     new_pipeline()
732    ///         .add(NewSessionMiddleware::default().with_session_type::<Session>())
733    ///         .build()
734    /// );
735    ///
736    /// build_router(chain, pipelines, |route| {
737    ///     // Requests dispatched to the `/api` router will not invoke the session middleware.
738    ///     route.delegate_without_pipelines("/api").to_router(api_router());
739    ///
740    ///     // Other requests will invoke the session middleware as normal.
741    ///     route.get("/").to(handler);
742    /// })
743    /// # }
744    /// #
745    /// # fn main() {
746    /// #   let test_server = TestServer::new(router()).unwrap();
747    /// #   let response = test_server.client()
748    /// #       .get("https://example.com/")
749    /// #       .perform()
750    /// #       .unwrap();
751    /// #   assert_eq!(response.status(), StatusCode::ACCEPTED);
752    /// #
753    /// #   let response = test_server.client()
754    /// #       .get("https://example.com/api")
755    /// #       .perform()
756    /// #       .unwrap();
757    /// #   assert_eq!(response.status(), StatusCode::ACCEPTED);
758    /// # }
759    /// ```
760    fn delegate_without_pipelines<'b>(
761        &'b mut self,
762        path: &str,
763    ) -> DelegateRouteBuilder<'b, AnyRouteMatcher, (), P> {
764        let (node_builder, _pipeline_chain, pipelines) = self.component_refs();
765        let node_builder = descend(node_builder, path);
766
767        DelegateRouteBuilder {
768            matcher: AnyRouteMatcher::new(),
769            node_builder,
770            pipeline_chain: (),
771            pipelines: pipelines.clone(),
772        }
773    }
774
775    /// Begins associating routes with a fixed path in the tree. In this way, multiple routes can
776    /// be quickly associated with a single location.
777    ///
778    /// # Examples
779    ///
780    /// ```rust
781    /// # extern crate gotham;
782    /// # extern crate hyper;
783    /// # extern crate mime;
784    /// #
785    /// # use hyper::{Body, Response, StatusCode};
786    /// # use gotham::router::Router;
787    /// # use gotham::router::builder::*;
788    /// # use gotham::state::State;
789    /// # use gotham::test::TestServer;
790    /// #
791    /// mod resource {
792    /// #   use super::*;
793    ///     pub fn show(state: State) -> (State, Response<Body>) {
794    ///         // Implementation elided.
795    /// #       (state, Response::builder().status(StatusCode::NO_CONTENT).body(Body::empty()).unwrap())
796    ///     }
797    ///
798    ///     pub fn update(state: State) -> (State, Response<Body>) {
799    ///         // Implementation elided.
800    /// #       (state, Response::builder().status(StatusCode::CREATED).body(Body::empty()).unwrap())
801    ///     }
802    ///
803    ///     pub fn delete(state: State) -> (State, Response<Body>) {
804    ///         // Implementation elided.
805    /// #       (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
806    ///     }
807    /// }
808    ///
809    /// #
810    /// # fn router() -> Router {
811    /// build_simple_router(|route| {
812    ///     route.associate("/resource", |assoc| {
813    ///         assoc.get_or_head().to(resource::show);
814    ///         assoc.patch().to(resource::update);
815    ///         assoc.delete().to(resource::delete);
816    ///     });
817    /// })
818    /// # }
819    /// #
820    /// # fn main() {
821    /// #   let test_server = TestServer::new(router()).unwrap();
822    /// #   let response = test_server.client()
823    /// #       .get("https://example.com/resource")
824    /// #       .perform()
825    /// #       .unwrap();
826    /// #   assert_eq!(response.status(), StatusCode::NO_CONTENT);
827    /// #
828    /// #   let response = test_server.client()
829    /// #       .patch("https://example.com/resource", b"".to_vec(), mime::TEXT_PLAIN)
830    /// #       .perform()
831    /// #       .unwrap();
832    /// #   assert_eq!(response.status(), StatusCode::CREATED);
833    /// #
834    /// #   let response = test_server.client()
835    /// #       .delete("https://example.com/resource")
836    /// #       .perform()
837    /// #       .unwrap();
838    /// #   assert_eq!(response.status(), StatusCode::ACCEPTED);
839    /// # }
840    /// ```
841    fn associate<'b, F>(&'b mut self, path: &str, f: F)
842    where
843        F: FnOnce(&mut DefaultAssociatedRouteBuilder<'b, AnyRouteMatcher, C, P>),
844    {
845        let (node_builder, pipeline_chain, pipelines) = self.component_refs();
846        let node_builder = descend(node_builder, path);
847
848        let mut builder =
849            AssociatedRouteBuilder::new(node_builder, *pipeline_chain, pipelines.clone());
850
851        f(&mut builder)
852    }
853
854    /// Return the components that comprise this builder. For internal use only.
855    #[doc(hidden)]
856    fn component_refs(&mut self) -> (&mut Node, &mut C, &PipelineSet<P>);
857}
858
859fn descend<'n>(node_builder: &'n mut Node, path: &str) -> &'n mut Node {
860    trace!("[walking to: {}]", path);
861
862    let path = path.strip_prefix('/').unwrap_or(path);
863    if path.is_empty() {
864        node_builder
865    } else {
866        build_subtree(node_builder, split_path_segments(path))
867    }
868}
869
870fn build_subtree<'n, 's, I>(node: &'n mut Node, mut i: I) -> &'n mut Node
871where
872    I: Iterator<Item = &'s str>,
873{
874    match i.next() {
875        Some(segment) => {
876            trace!("[descending into {}]", segment);
877
878            let (segment, segment_type) = match segment.chars().next() {
879                Some(':') => {
880                    let segment = &segment[1..];
881                    match segment.find(':') {
882                        Some(n) => {
883                            let (segment, pattern) = segment.split_at(n);
884                            let regex = Box::new(ConstrainedSegmentRegex::new(&pattern[1..]));
885                            (segment, SegmentType::Constrained { regex })
886                        }
887                        None => (segment, SegmentType::Dynamic),
888                    }
889                }
890                Some('*') if segment.len() == 1 => (segment, SegmentType::Glob),
891                Some('*') => (&segment[1..], SegmentType::Glob),
892                Some('\\') => (&segment[1..], SegmentType::Static),
893                _ => (segment, SegmentType::Static),
894            };
895
896            if !node.has_child(segment, segment_type.clone()) {
897                node.add_child(Node::new(segment, segment_type.clone()));
898            }
899
900            let child = node.borrow_child_mut(segment, segment_type).unwrap();
901            build_subtree(child, i)
902        }
903        None => {
904            trace!("[reached node]");
905            node
906        }
907    }
908}
909
910impl<'a, C, P> DrawRoutes<C, P> for RouterBuilder<'a, C, P>
911where
912    C: PipelineHandleChain<P> + Copy + Send + Sync + 'static,
913    P: RefUnwindSafe + Send + Sync + 'static,
914{
915    fn component_refs(&mut self) -> (&mut Node, &mut C, &PipelineSet<P>) {
916        (self.node_builder, &mut self.pipeline_chain, &self.pipelines)
917    }
918}
919
920impl<'a, C, P> DrawRoutes<C, P> for ScopeBuilder<'a, C, P>
921where
922    C: PipelineHandleChain<P> + Copy + Send + Sync + 'static,
923    P: RefUnwindSafe + Send + Sync + 'static,
924{
925    fn component_refs(&mut self) -> (&mut Node, &mut C, &PipelineSet<P>) {
926        (self.node_builder, &mut self.pipeline_chain, &self.pipelines)
927    }
928}
929
930#[cfg(test)]
931mod tests {
932    use std::pin::Pin;
933
934    use futures_util::future::{self, FutureExt};
935    use hyper::{Body, Response, StatusCode};
936
937    use crate::handler::HandlerFuture;
938    use crate::helpers::http::response::create_empty_response;
939    use crate::hyper::header::ACCEPT;
940    use crate::middleware::{Middleware, NewMiddleware};
941    use crate::pipeline::*;
942    use crate::router::builder::*;
943    use crate::router::route::matcher::AcceptHeaderRouteMatcher;
944    use crate::state::State;
945    use crate::test::TestServer;
946
947    #[derive(Clone, Copy)]
948    struct QuickExitMiddleware;
949
950    impl NewMiddleware for QuickExitMiddleware {
951        type Instance = Self;
952
953        fn new_middleware(&self) -> anyhow::Result<Self> {
954            Ok(*self)
955        }
956    }
957
958    impl Middleware for QuickExitMiddleware {
959        fn call<Chain>(self, state: State, _chain: Chain) -> Pin<Box<HandlerFuture>>
960        where
961            Chain: FnOnce(State) -> Pin<Box<HandlerFuture>> + 'static,
962        {
963            let f = future::ok((
964                state,
965                Response::builder()
966                    .status(StatusCode::INTERNAL_SERVER_ERROR)
967                    .body(Body::empty())
968                    .unwrap(),
969            ));
970
971            f.boxed()
972        }
973    }
974
975    fn test_handler(state: State) -> (State, Response<Body>) {
976        let response = create_empty_response(&state, StatusCode::ACCEPTED);
977        (state, response)
978    }
979
980    #[test]
981    fn delegate_with_matcher() {
982        let test_router = build_simple_router(|route| {
983            route.get("/").to(test_handler);
984        });
985
986        let router = build_simple_router(|route| {
987            let matcher = AcceptHeaderRouteMatcher::new(vec![mime::APPLICATION_JSON]);
988            route
989                .delegate("/test")
990                .add_route_matcher(matcher)
991                .to_router(test_router);
992        });
993
994        let test_server = TestServer::new(router).unwrap();
995        let response = test_server
996            .client()
997            .get("http://localhost/test/")
998            .with_header(ACCEPT, mime::JAVASCRIPT.to_string().parse().unwrap())
999            .perform()
1000            .unwrap();
1001        assert_eq!(response.status(), StatusCode::NOT_ACCEPTABLE);
1002        let response = test_server
1003            .client()
1004            .get("http://localhost/test/")
1005            .with_header(ACCEPT, mime::APPLICATION_JSON.to_string().parse().unwrap())
1006            .perform()
1007            .unwrap();
1008        assert_eq!(response.status(), StatusCode::ACCEPTED);
1009    }
1010
1011    #[test]
1012    fn delegate_includes_pipelines() {
1013        let (chain, pipelines) = single_pipeline(new_pipeline().add(QuickExitMiddleware).build());
1014
1015        let test_router = build_simple_router(|route| {
1016            route.get("/").to(test_handler);
1017        });
1018
1019        let router = build_router(chain, pipelines, |route| {
1020            route.delegate("/test").to_router(test_router);
1021        });
1022
1023        let test_server = TestServer::new(router).unwrap();
1024        let response = test_server
1025            .client()
1026            .get("http://localhost/test/")
1027            .perform()
1028            .unwrap();
1029        assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
1030    }
1031
1032    #[test]
1033    fn delegate_without_pipelines_skips_pipelines() {
1034        let (chain, pipelines) = single_pipeline(new_pipeline().add(QuickExitMiddleware).build());
1035
1036        let test_router = build_simple_router(|route| {
1037            route.get("/").to(test_handler);
1038        });
1039
1040        let router = build_router(chain, pipelines, |route| {
1041            route
1042                .delegate_without_pipelines("/test")
1043                .to_router(test_router);
1044        });
1045
1046        let test_server = TestServer::new(router).unwrap();
1047        let response = test_server
1048            .client()
1049            .get("http://localhost/test/")
1050            .perform()
1051            .unwrap();
1052
1053        assert_eq!(response.status(), StatusCode::ACCEPTED);
1054    }
1055}