openapi_context/
request_parser.rs

1//! Methods for retrieving swagger-related information from an HTTP request.
2use hyper::Request;
3
4/// A macro for joining together two or more RequestParsers to create a struct that implements
5/// RequestParser with a function parse_operation_id that matches hyper requests against the different
6/// RequestParsers in turn until it gets a match (or returns an error if none match)
7///
8/// The order in which the request parsers are passed to the macro specifies the order in which the request
9/// is tested against them. If there is any possibility of two RequestParsers matching the same request
10/// this should not be used.
11#[macro_export]
12macro_rules! request_parser_joiner {
13    ($name:ident ,$($T:ty), *) => {
14        struct $name;
15
16        impl <B> RequestParser<B> for $name
17            where $($T: RequestParser<B>, )*
18        {
19            fn parse_operation_id(request: &hyper::Request<B>) -> Result<&'static str, ()> {
20                __impl_request_parser_joiner!(request, $($T), *)
21            }
22        }
23    };
24}
25
26/// This macro should only be used by the request_parser_joiner macro
27#[macro_export]
28#[doc(hidden)]
29macro_rules! __impl_request_parser_joiner {
30    ($argname:expr, $head:ty) => {<$head as RequestParser<B>>::parse_operation_id(&$argname)};
31    ($argname:expr, $head:ty, $( $tail:ty), *) => {
32        match <$head as RequestParser<B>>::parse_operation_id(&$argname) {
33                Ok(s) => Ok(s),
34                Err(_) => __impl_request_parser_joiner!($argname, $( $tail), *),
35        }
36    };
37}
38
39/// A trait for retrieving swagger-related information from a request.
40///
41/// This allows other middlewares to retrieve API-related information from a request that
42/// may not have been handled by the autogenerated API code yet.   For example, a statistics
43/// tracking service may wish to use this to count requests per-operation.
44///
45/// The trait is automatically implemented by swagger-codegen.
46pub trait RequestParser<B> {
47    /// Retrieve the Swagger operation identifier that matches this request.
48    ///
49    /// Returns `Err(())` if this request does not match any known operation on this API.
50    fn parse_operation_id(req: &Request<B>) -> Result<&'static str, ()>;
51}
52
53#[cfg(test)]
54mod context_tests {
55    use super::*;
56    use hyper::{Body, Uri};
57    use std::str::FromStr;
58
59    struct TestParser1;
60
61    impl RequestParser<Body> for TestParser1 {
62        fn parse_operation_id(request: &hyper::Request<Body>) -> Result<&'static str, ()> {
63            match request.uri().path() {
64                "/test/t11" => Ok("t11"),
65                "/test/t12" => Ok("t12"),
66                _ => Err(()),
67            }
68        }
69    }
70
71    struct TestParser2;
72
73    impl RequestParser<Body> for TestParser2 {
74        fn parse_operation_id(request: &hyper::Request<Body>) -> Result<&'static str, ()> {
75            match request.uri().path() {
76                "/test/t21" => Ok("t21"),
77                "/test/t22" => Ok("t22"),
78                _ => Err(()),
79            }
80        }
81    }
82
83    #[test]
84    fn test_macros() {
85        let uri = Uri::from_str(&"https://www.rust-lang.org/test/t11").unwrap();
86        let req1: Request<Body> = Request::get(uri).body(Body::empty()).unwrap();
87
88        let uri = Uri::from_str(&"https://www.rust-lang.org/test/t22").unwrap();
89        let req2: Request<Body> = Request::get(uri).body(Body::empty()).unwrap();
90
91        let uri = Uri::from_str(&"https://www.rust-lang.org/test/t33").unwrap();
92        let req3: Request<Body> = Request::get(uri).body(Body::empty()).unwrap();
93
94        request_parser_joiner!(JoinedReqParser, TestParser1, TestParser2);
95
96        assert_eq!(JoinedReqParser::parse_operation_id(&req1), Ok("t11"));
97        assert_eq!(JoinedReqParser::parse_operation_id(&req2), Ok("t22"));
98        assert_eq!(JoinedReqParser::parse_operation_id(&req3), Err(()));
99    }
100}