gotham/router/builder/associated.rs
1use std::marker::PhantomData;
2use std::panic::RefUnwindSafe;
3
4use hyper::{Body, Method};
5
6use crate::extractor::{PathExtractor, QueryStringExtractor};
7use crate::pipeline::{PipelineHandleChain, PipelineSet};
8use crate::router::builder::SingleRouteBuilder;
9use crate::router::route::matcher::{
10 AndRouteMatcher, AnyRouteMatcher, MethodOnlyRouteMatcher, RouteMatcher,
11};
12use crate::router::tree::node::Node;
13
14pub(crate) type AssociatedRouteBuilderMatcher<M, NM> = AndRouteMatcher<M, NM>;
15pub(crate) type AssociatedRouteMatcher<M> = AndRouteMatcher<MethodOnlyRouteMatcher, M>;
16
17/// The default type returned when building a single associated route. See
18/// `router::builder::DefineSingleRoute` for an overview of the ways that a route can be specified.
19pub type AssociatedSingleRouteBuilder<'a, M, C, P, PE, QSE> =
20 SingleRouteBuilder<'a, M, C, P, PE, QSE>;
21
22/// Implements the methods required for associating a number of routes with a single path. This is
23/// used by `DrawRoutes::associated`.
24pub struct AssociatedRouteBuilder<'a, M, C, P, PE, QSE>
25where
26 M: RouteMatcher + Send + Sync + 'static,
27 C: PipelineHandleChain<P> + Copy + Send + Sync + 'static,
28 P: RefUnwindSafe + Send + Sync + 'static,
29 PE: PathExtractor<Body> + Send + Sync + 'static,
30 QSE: QueryStringExtractor<Body> + Send + Sync + 'static,
31{
32 node_builder: &'a mut Node,
33 matcher: M,
34 pipeline_chain: C,
35 pipelines: PipelineSet<P>,
36 phantom: PhantomData<(PE, QSE)>,
37}
38
39impl<'a, C, P, PE, QSE> AssociatedRouteBuilder<'a, AnyRouteMatcher, C, P, PE, QSE>
40where
41 C: PipelineHandleChain<P> + Copy + Send + Sync + 'static,
42 P: RefUnwindSafe + Send + Sync + 'static,
43 PE: PathExtractor<Body> + Send + Sync + 'static,
44 QSE: QueryStringExtractor<Body> + Send + Sync + 'static,
45{
46 /// Create an instance of AssociatedRouteBuilder
47 pub fn new(node_builder: &'a mut Node, pipeline_chain: C, pipelines: PipelineSet<P>) -> Self {
48 AssociatedRouteBuilder {
49 node_builder,
50 matcher: AnyRouteMatcher::new(),
51 pipeline_chain,
52 pipelines,
53 phantom: PhantomData,
54 }
55 }
56}
57
58impl<'a, M, C, P, PE, QSE> AssociatedRouteBuilder<'a, M, C, P, PE, QSE>
59where
60 M: RouteMatcher + Send + Sync + 'static,
61 C: PipelineHandleChain<P> + Copy + Send + Sync + 'static,
62 P: RefUnwindSafe + Send + Sync + 'static,
63 PE: PathExtractor<Body> + Send + Sync + 'static,
64 QSE: QueryStringExtractor<Body> + Send + Sync + 'static,
65{
66 /// Adds aadditional `RouteMatcher` requirements to all subsequently associated routes.
67 ///
68 /// # Examples
69 ///
70 /// ```
71 /// # use hyper::{Body, Response, StatusCode};
72 /// # use hyper::header::ACCEPT;
73 /// # use gotham::state::State;
74 /// # use gotham::router::route::matcher::AcceptHeaderRouteMatcher;
75 /// # use gotham::router::{build_simple_router, Router};
76 /// # use gotham::prelude::*;
77 /// # use gotham::test::TestServer;
78 /// #
79 /// # fn my_handler(state: State) -> (State, Response<Body>) {
80 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
81 /// # }
82 /// #
83 /// # fn router() -> Router {
84 /// build_simple_router(|route| {
85 /// let matcher = AcceptHeaderRouteMatcher::new(vec![mime::APPLICATION_JSON]);
86 ///
87 /// route.associate("/resource/path", |assoc| {
88 /// let mut assoc = assoc.add_route_matcher(matcher);
89 ///
90 /// assoc.get().to(my_handler);
91 /// });
92 /// })
93 /// # }
94 /// #
95 /// # fn main() {
96 /// # let test_server = TestServer::new(router()).unwrap();
97 /// #
98 /// # let response = test_server.client()
99 /// # .get("https://example.com/resource/path")
100 /// # .with_header(ACCEPT, mime::APPLICATION_JSON.to_string().parse().unwrap())
101 /// # .perform()
102 /// # .unwrap();
103 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
104 /// #
105 /// # let response = test_server.client()
106 /// # .get("https://example.com/resource/path")
107 /// # .with_header(ACCEPT, mime::TEXT_PLAIN.to_string().parse().unwrap())
108 /// # .perform()
109 /// # .unwrap();
110 /// # assert_eq!(response.status(), StatusCode::NOT_ACCEPTABLE);
111 /// # }
112 /// ```
113 pub fn add_route_matcher<'b, NM>(
114 &'b mut self,
115 matcher: NM,
116 ) -> AssociatedRouteBuilder<'b, AssociatedRouteBuilderMatcher<M, NM>, C, P, PE, QSE>
117 where
118 NM: RouteMatcher + Send + Sync + 'static,
119 {
120 let matcher = AndRouteMatcher::new(self.matcher.clone(), matcher);
121 AssociatedRouteBuilder {
122 node_builder: self.node_builder,
123 matcher,
124 pipeline_chain: self.pipeline_chain,
125 pipelines: self.pipelines.clone(),
126 phantom: PhantomData,
127 }
128 }
129
130 /// Binds a new `PathExtractor` to the associated routes.
131 ///
132 /// # Examples
133 ///
134 /// ```rust
135 /// # use hyper::{Body, Response, StatusCode};
136 /// # use gotham::router::{build_simple_router, Router};
137 /// # use gotham::prelude::*;
138 /// # use gotham::state::State;
139 /// # use gotham::test::TestServer;
140 /// # use serde::Deserialize;
141 /// #
142 /// fn handler(state: State) -> (State, Response<Body>) {
143 /// // Implementation elided.
144 /// # assert_eq!(state.borrow::<MyPathExtractor>().id, 42);
145 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
146 /// }
147 ///
148 /// #[derive(Deserialize, StateData, StaticResponseExtender)]
149 /// struct MyPathExtractor {
150 /// # #[allow(dead_code)]
151 /// id: u32,
152 /// }
153 ///
154 /// #
155 /// # fn router() -> Router {
156 /// build_simple_router(|route| {
157 /// route.associate("/resource/:id", |assoc| {
158 /// let mut assoc = assoc.with_path_extractor::<MyPathExtractor>();
159 /// assoc.get().to(handler);
160 /// });
161 /// })
162 /// # }
163 /// #
164 /// # fn main() {
165 /// # let test_server = TestServer::new(router()).unwrap();
166 /// # let response = test_server.client()
167 /// # .get("https://example.com/resource/42")
168 /// # .perform()
169 /// # .unwrap();
170 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
171 /// # }
172 /// ```
173 pub fn with_path_extractor<'b, NPE>(
174 &'b mut self,
175 ) -> AssociatedRouteBuilder<'b, M, C, P, NPE, QSE>
176 where
177 NPE: PathExtractor<Body> + Send + Sync + 'static,
178 {
179 AssociatedRouteBuilder {
180 node_builder: self.node_builder,
181 matcher: self.matcher.clone(),
182 pipeline_chain: self.pipeline_chain,
183 pipelines: self.pipelines.clone(),
184 phantom: PhantomData,
185 }
186 }
187
188 /// Binds a new `QueryStringExtractor` to the associated routes.
189 ///
190 /// # Examples
191 ///
192 /// ```rust
193 /// # use hyper::{Body, Response, StatusCode};
194 /// # use gotham::router::{build_simple_router, Router};
195 /// # use gotham::prelude::*;
196 /// # use gotham::state::State;
197 /// # use gotham::test::TestServer;
198 /// # use serde::Deserialize;
199 /// #
200 /// fn handler(state: State) -> (State, Response<Body>) {
201 /// // Implementation elided.
202 /// # assert_eq!(state.borrow::<MyQueryStringExtractor>().val.as_str(), "test_val");
203 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
204 /// }
205 ///
206 /// #[derive(StateData, Deserialize, StaticResponseExtender)]
207 /// struct MyQueryStringExtractor {
208 /// # #[allow(dead_code)]
209 /// val: String,
210 /// }
211 ///
212 /// #
213 /// # fn router() -> Router {
214 /// build_simple_router(|route| {
215 /// route.associate("/resource", |assoc| {
216 /// let mut assoc = assoc.with_query_string_extractor::<MyQueryStringExtractor>();
217 /// assoc.get().to(handler);
218 /// });
219 /// })
220 /// # }
221 /// #
222 /// # fn main() {
223 /// # let test_server = TestServer::new(router()).unwrap();
224 /// # let response = test_server.client()
225 /// # .get("https://example.com/resource?val=test_val")
226 /// # .perform()
227 /// # .unwrap();
228 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
229 /// # }
230 /// ```
231 pub fn with_query_string_extractor<'b, NQSE>(
232 &'b mut self,
233 ) -> AssociatedRouteBuilder<'b, M, C, P, PE, NQSE>
234 where
235 NQSE: QueryStringExtractor<Body> + Send + Sync + 'static,
236 {
237 AssociatedRouteBuilder {
238 node_builder: self.node_builder,
239 matcher: self.matcher.clone(),
240 pipeline_chain: self.pipeline_chain,
241 pipelines: self.pipelines.clone(),
242 phantom: PhantomData,
243 }
244 }
245
246 /// Associates a route which matches requests with any of the specified methods, to the current
247 /// path.
248 ///
249 /// # Examples
250 ///
251 /// ```rust
252 /// # extern crate gotham;
253 /// # extern crate hyper;
254 /// # extern crate mime;
255 /// #
256 /// # use hyper::{Body, Response, Method, StatusCode};
257 /// # use gotham::router::Router;
258 /// # use gotham::router::builder::*;
259 /// # use gotham::state::State;
260 /// # use gotham::test::TestServer;
261 /// #
262 /// fn handler(state: State) -> (State, Response<Body>) {
263 /// // Implementation elided.
264 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
265 /// }
266 ///
267 /// #
268 /// # fn router() -> Router {
269 /// build_simple_router(|route| {
270 /// route.associate("/resource", |assoc| {
271 /// assoc.request(vec![Method::GET, Method::HEAD, Method::POST]).to(handler);
272 /// });
273 /// })
274 /// # }
275 /// #
276 /// # fn main() {
277 /// # let test_server = TestServer::new(router()).unwrap();
278 /// #
279 /// # let response = test_server.client()
280 /// # .get("https://example.com/resource")
281 /// # .perform()
282 /// # .unwrap();
283 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
284 /// #
285 /// # let response = test_server.client()
286 /// # .head("https://example.com/resource")
287 /// # .perform()
288 /// # .unwrap();
289 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
290 /// #
291 /// # let response = test_server.client()
292 /// # .post("https://example.com/resource", b"".to_vec(), mime::TEXT_PLAIN)
293 /// # .perform()
294 /// # .unwrap();
295 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
296 /// # }
297 /// ```
298 pub fn request<'b>(
299 &'b mut self,
300 methods: Vec<Method>,
301 ) -> AssociatedSingleRouteBuilder<'b, AssociatedRouteMatcher<M>, C, P, PE, QSE> {
302 let AssociatedRouteBuilder {
303 ref mut node_builder,
304 ref matcher,
305 ref pipeline_chain,
306 ref pipelines,
307 phantom,
308 } = *self;
309
310 SingleRouteBuilder {
311 node_builder,
312 matcher: AndRouteMatcher::new(MethodOnlyRouteMatcher::new(methods), matcher.clone()),
313 pipeline_chain: *pipeline_chain,
314 pipelines: pipelines.clone(),
315 phantom,
316 }
317 }
318
319 /// Associates a route which matches `HEAD` requests to the current 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::router::Router;
329 /// # use gotham::router::builder::*;
330 /// # use gotham::state::State;
331 /// # use gotham::test::TestServer;
332 /// #
333 /// fn handler(state: State) -> (State, Response<Body>) {
334 /// // Implementation elided.
335 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
336 /// }
337 ///
338 /// #
339 /// # fn router() -> Router {
340 /// build_simple_router(|route| {
341 /// route.associate("/resource", |assoc| {
342 /// assoc.head().to(handler);
343 /// });
344 /// })
345 /// # }
346 /// #
347 /// # fn main() {
348 /// # let test_server = TestServer::new(router()).unwrap();
349 /// # let response = test_server.client()
350 /// # .head("https://example.com/resource")
351 /// # .perform()
352 /// # .unwrap();
353 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
354 /// # }
355 /// ```
356 pub fn head<'b>(
357 &'b mut self,
358 ) -> AssociatedSingleRouteBuilder<'b, AssociatedRouteMatcher<M>, C, P, PE, QSE> {
359 self.request(vec![Method::HEAD])
360 }
361
362 /// Associates a route which matches `GET` or `HEAD` requests to the current path.
363 ///
364 /// # Examples
365 ///
366 /// ```rust
367 /// # extern crate gotham;
368 /// # extern crate hyper;
369 /// #
370 /// # use hyper::{Body, Response, StatusCode};
371 /// # use gotham::router::Router;
372 /// # use gotham::router::builder::*;
373 /// # use gotham::state::State;
374 /// # use gotham::test::TestServer;
375 /// #
376 /// fn handler(state: State) -> (State, Response<Body>) {
377 /// // Implementation elided.
378 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
379 /// }
380 ///
381 /// #
382 /// # fn router() -> Router {
383 /// build_simple_router(|route| {
384 /// route.associate("/resource", |assoc| {
385 /// assoc.get_or_head().to(handler);
386 /// });
387 /// })
388 /// # }
389 /// #
390 /// # fn main() {
391 /// # let test_server = TestServer::new(router()).unwrap();
392 /// #
393 /// # let response = test_server.client()
394 /// # .get("https://example.com/resource")
395 /// # .perform()
396 /// # .unwrap();
397 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
398 /// #
399 /// # let response = test_server.client()
400 /// # .head("https://example.com/resource")
401 /// # .perform()
402 /// # .unwrap();
403 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
404 /// # }
405 /// ```
406 pub fn get_or_head<'b>(
407 &'b mut self,
408 ) -> AssociatedSingleRouteBuilder<'b, AssociatedRouteMatcher<M>, C, P, PE, QSE> {
409 self.request(vec![Method::GET, Method::HEAD])
410 }
411
412 /// Associates a route which matches `GET` requests to the current path.
413 ///
414 /// # Examples
415 ///
416 /// ```rust
417 /// # extern crate gotham;
418 /// # extern crate hyper;
419 /// #
420 /// # use hyper::{Body, Response, StatusCode};
421 /// # use gotham::router::Router;
422 /// # use gotham::router::builder::*;
423 /// # use gotham::state::State;
424 /// # use gotham::test::TestServer;
425 /// #
426 /// fn handler(state: State) -> (State, Response<Body>) {
427 /// // Implementation elided.
428 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
429 /// }
430 ///
431 /// #
432 /// # fn router() -> Router {
433 /// build_simple_router(|route| {
434 /// route.associate("/resource", |assoc| {
435 /// assoc.get().to(handler);
436 /// });
437 /// })
438 /// # }
439 /// #
440 /// # fn main() {
441 /// # let test_server = TestServer::new(router()).unwrap();
442 /// # let response = test_server.client()
443 /// # .get("https://example.com/resource")
444 /// # .perform()
445 /// # .unwrap();
446 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
447 /// # }
448 /// ```
449 pub fn get<'b>(
450 &'b mut self,
451 ) -> AssociatedSingleRouteBuilder<'b, AssociatedRouteMatcher<M>, C, P, PE, QSE> {
452 self.request(vec![Method::GET])
453 }
454
455 /// Associates a route which matches `POST` requests to the current path.
456 ///
457 /// # Examples
458 ///
459 /// ```rust
460 /// # extern crate gotham;
461 /// # extern crate hyper;
462 /// # extern crate mime;
463 /// #
464 /// # use hyper::{Body, Response, StatusCode};
465 /// # use gotham::router::Router;
466 /// # use gotham::router::builder::*;
467 /// # use gotham::state::State;
468 /// # use gotham::test::TestServer;
469 /// #
470 /// fn handler(state: State) -> (State, Response<Body>) {
471 /// // Implementation elided.
472 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
473 /// }
474 ///
475 /// #
476 /// # fn router() -> Router {
477 /// build_simple_router(|route| {
478 /// route.associate("/resource", |assoc| {
479 /// assoc.post().to(handler);
480 /// });
481 /// })
482 /// # }
483 /// #
484 /// # fn main() {
485 /// # let test_server = TestServer::new(router()).unwrap();
486 /// # let response = test_server.client()
487 /// # .post("https://example.com/resource", b"".to_vec(), mime::TEXT_PLAIN)
488 /// # .perform()
489 /// # .unwrap();
490 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
491 /// # }
492 /// ```
493 pub fn post<'b>(
494 &'b mut self,
495 ) -> AssociatedSingleRouteBuilder<'b, AssociatedRouteMatcher<M>, C, P, PE, QSE> {
496 self.request(vec![Method::POST])
497 }
498
499 /// Associates a route which matches `PUT` requests to the current path.
500 ///
501 /// # Examples
502 ///
503 /// ```rust
504 /// # extern crate gotham;
505 /// # extern crate hyper;
506 /// # extern crate mime;
507 /// #
508 /// # use hyper::{Body, Response, StatusCode};
509 /// # use gotham::router::Router;
510 /// # use gotham::router::builder::*;
511 /// # use gotham::state::State;
512 /// # use gotham::test::TestServer;
513 /// #
514 /// fn handler(state: State) -> (State, Response<Body>) {
515 /// // Implementation elided.
516 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
517 /// }
518 ///
519 /// #
520 /// # fn router() -> Router {
521 /// build_simple_router(|route| {
522 /// route.associate("/resource", |assoc| {
523 /// assoc.put().to(handler);
524 /// });
525 /// })
526 /// # }
527 /// #
528 /// # fn main() {
529 /// # let test_server = TestServer::new(router()).unwrap();
530 /// # let response = test_server.client()
531 /// # .put("https://example.com/resource", b"".to_vec(), mime::TEXT_PLAIN)
532 /// # .perform()
533 /// # .unwrap();
534 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
535 /// # }
536 /// ```
537 pub fn put<'b>(
538 &'b mut self,
539 ) -> AssociatedSingleRouteBuilder<'b, AssociatedRouteMatcher<M>, C, P, PE, QSE> {
540 self.request(vec![Method::PUT])
541 }
542
543 /// Associates a route which matches `PATCH` requests to the current path.
544 ///
545 /// # Examples
546 ///
547 /// ```rust
548 /// # extern crate gotham;
549 /// # extern crate hyper;
550 /// # extern crate mime;
551 /// #
552 /// # use hyper::{Body, Response, StatusCode};
553 /// # use gotham::router::Router;
554 /// # use gotham::router::builder::*;
555 /// # use gotham::state::State;
556 /// # use gotham::test::TestServer;
557 /// #
558 /// fn handler(state: State) -> (State, Response<Body>) {
559 /// // Implementation elided.
560 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
561 /// }
562 ///
563 /// #
564 /// # fn router() -> Router {
565 /// build_simple_router(|route| {
566 /// route.associate("/resource", |assoc| {
567 /// assoc.patch().to(handler);
568 /// });
569 /// })
570 /// # }
571 /// #
572 /// # fn main() {
573 /// # let test_server = TestServer::new(router()).unwrap();
574 /// # let response = test_server.client()
575 /// # .patch("https://example.com/resource", b"".to_vec(), mime::TEXT_PLAIN)
576 /// # .perform()
577 /// # .unwrap();
578 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
579 /// # }
580 /// ```
581 pub fn patch<'b>(
582 &'b mut self,
583 ) -> AssociatedSingleRouteBuilder<'b, AssociatedRouteMatcher<M>, C, P, PE, QSE> {
584 self.request(vec![Method::PATCH])
585 }
586
587 /// Associates a route which matches `DELETE` requests to the current path.
588 ///
589 /// # Examples
590 ///
591 /// ```rust
592 /// # extern crate gotham;
593 /// # extern crate hyper;
594 /// #
595 /// # use hyper::{Body, Response, StatusCode};
596 /// # use gotham::router::Router;
597 /// # use gotham::router::builder::*;
598 /// # use gotham::state::State;
599 /// # use gotham::test::TestServer;
600 /// #
601 /// fn handler(state: State) -> (State, Response<Body>) {
602 /// // Implementation elided.
603 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
604 /// }
605 ///
606 /// #
607 /// # fn router() -> Router {
608 /// build_simple_router(|route| {
609 /// route.associate("/resource", |assoc| {
610 /// assoc.delete().to(handler);
611 /// });
612 /// })
613 /// # }
614 /// #
615 /// # fn main() {
616 /// # let test_server = TestServer::new(router()).unwrap();
617 /// # let response = test_server.client()
618 /// # .delete("https://example.com/resource")
619 /// # .perform()
620 /// # .unwrap();
621 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
622 /// # }
623 /// ```
624 pub fn delete<'b>(
625 &'b mut self,
626 ) -> AssociatedSingleRouteBuilder<'b, AssociatedRouteMatcher<M>, C, P, PE, QSE> {
627 self.request(vec![Method::DELETE])
628 }
629
630 /// Associates a route which matches `OPTIONS` requests to the current path.
631 ///
632 /// # Examples
633 ///
634 /// ```rust
635 /// # extern crate gotham;
636 /// # extern crate hyper;
637 /// #
638 /// # use hyper::{Body, Response, StatusCode};
639 /// # use gotham::router::Router;
640 /// # use gotham::router::builder::*;
641 /// # use gotham::state::State;
642 /// # use gotham::test::TestServer;
643 /// #
644 /// fn handler(state: State) -> (State, Response<Body>) {
645 /// // Implementation elided.
646 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
647 /// }
648 ///
649 /// #
650 /// # fn router() -> Router {
651 /// build_simple_router(|route| {
652 /// route.associate("/resource", |assoc| {
653 /// assoc.options().to(handler);
654 /// });
655 /// })
656 /// # }
657 /// #
658 /// # fn main() {
659 /// # let test_server = TestServer::new(router()).unwrap();
660 /// # let response = test_server.client()
661 /// # .options("https://example.com/resource")
662 /// # .perform()
663 /// # .unwrap();
664 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
665 /// # }
666 /// ```
667 pub fn options<'b>(
668 &'b mut self,
669 ) -> AssociatedSingleRouteBuilder<'b, AssociatedRouteMatcher<M>, C, P, PE, QSE> {
670 self.request(vec![Method::OPTIONS])
671 }
672}