routerify/ext/
request.rs

1use crate::data_map::SharedDataMap;
2use crate::types::{RequestContext, RequestMeta, RouteParams};
3use hyper::Request;
4use std::net::SocketAddr;
5
6/// A extension trait which extends the [`hyper::Request`](https://docs.rs/hyper/0.14.4/hyper/struct.Request.html) and [`http::Parts`](https://docs.rs/http/0.2.4/http/request/struct.Parts.html) types with some helpful methods.
7pub trait RequestExt {
8    /// It returns the route parameters as [RouteParams](../struct.RouteParams.html) type with the name of the parameter specified in the path as their respective keys.
9    ///
10    /// # Examples
11    ///
12    /// ```
13    /// use routerify::{Router, RouteParams};
14    /// use routerify::ext::RequestExt;
15    /// use hyper::{Response, Body};
16    /// # use std::convert::Infallible;
17    ///
18    /// # fn run() -> Router<Body, Infallible> {
19    /// let router = Router::builder()
20    ///     .get("/users/:userName/books/:bookName", |req| async move {
21    ///         let params: &RouteParams = req.params();
22    ///         let user_name = params.get("userName").unwrap();
23    ///         let book_name = params.get("bookName").unwrap();
24    ///
25    ///         Ok(Response::new(Body::from(format!("Username: {}, Book Name: {}", user_name, book_name))))
26    ///      })
27    ///      .build()
28    ///      .unwrap();
29    /// # router
30    /// # }
31    /// # run();
32    /// ```
33    fn params(&self) -> &RouteParams;
34
35    /// It returns the route parameter value by the name of the parameter specified in the path.
36    ///
37    /// # Examples
38    ///
39    /// ```
40    /// use routerify::{Router, RouteParams};
41    /// use routerify::ext::RequestExt;
42    /// use hyper::{Response, Body};
43    /// # use std::convert::Infallible;
44    ///
45    /// # fn run() -> Router<Body, Infallible> {
46    /// let router = Router::builder()
47    ///     .get("/users/:userName/books/:bookName", |req| async move {
48    ///         let user_name = req.param("userName").unwrap();
49    ///         let book_name = req.param("bookName").unwrap();
50    ///
51    ///         Ok(Response::new(Body::from(format!("Username: {}, Book Name: {}", user_name, book_name))))
52    ///      })
53    ///      .build()
54    ///      .unwrap();
55    /// # router
56    /// # }
57    /// # run();
58    /// ```
59    fn param<P: Into<String>>(&self, param_name: P) -> Option<&String>;
60
61    /// It returns the remote address of the incoming request.
62    ///
63    /// # Examples
64    ///
65    /// ```
66    /// use routerify::{Router, RouteParams};
67    /// use routerify::ext::RequestExt;
68    /// use hyper::{Response, Body};
69    /// # use std::convert::Infallible;
70    ///
71    /// # fn run() -> Router<Body, Infallible> {
72    /// let router = Router::builder()
73    ///     .get("/hello", |req| async move {
74    ///         let remote_addr = req.remote_addr();
75    ///
76    ///         Ok(Response::new(Body::from(format!("Hello from : {}", remote_addr))))
77    ///      })
78    ///      .build()
79    ///      .unwrap();
80    /// # router
81    /// # }
82    /// # run();
83    /// ```
84    fn remote_addr(&self) -> SocketAddr;
85
86    /// Access data which was shared by the [`RouterBuilder`](../struct.RouterBuilder.html) method
87    /// [`data`](../struct.RouterBuilder.html#method.data).
88    ///
89    /// Please refer to the [Data and State Sharing](../index.html#data-and-state-sharing) for more info.
90    fn data<T: Send + Sync + 'static>(&self) -> Option<&T>;
91
92    /// Access data in the request context.
93    fn context<T: Send + Sync + Clone + 'static>(&self) -> Option<T>;
94
95    /// Put data into the request context.
96    ///
97    /// # Examples
98    ///
99    /// ```
100    /// use routerify::{Router, RouteParams, Middleware};
101    /// use routerify::ext::RequestExt;
102    /// use hyper::{Response, Request, Body};
103    /// # use std::convert::Infallible;
104    ///
105    /// # fn run() -> Router<Body, Infallible> {
106    /// let router = Router::builder()
107    ///     .middleware(Middleware::pre(|req: Request<Body>| async move {
108    ///         req.set_context("example".to_string());
109    ///
110    ///         Ok(req)
111    ///     }))
112    ///     .get("/hello", |req| async move {
113    ///         let text = req.context::<String>().unwrap();
114    ///
115    ///         Ok(Response::new(Body::from(format!("Hello from : {}", text))))
116    ///      })
117    ///      .build()
118    ///      .unwrap();
119    /// # router
120    /// # }
121    /// # run();
122    /// ```
123    fn set_context<T: Send + Sync + Clone + 'static>(&self, val: T);
124}
125
126fn params(ext: &http::Extensions) -> &RouteParams {
127    ext.get::<RequestMeta>()
128        .and_then(|meta| meta.route_params())
129        .expect("Routerify: No RouteParams added while processing request")
130}
131
132fn param<P: Into<String>>(ext: &http::Extensions, param_name: P) -> Option<&String> {
133    params(ext).get(&param_name.into())
134}
135
136fn remote_addr(ext: &http::Extensions) -> SocketAddr {
137    ext.get::<RequestMeta>()
138        .and_then(|meta| meta.remote_addr())
139        .copied()
140        .expect("Routerify: No remote address added while processing request")
141}
142
143fn data<T: Send + Sync + 'static>(ext: &http::Extensions) -> Option<&T> {
144    let shared_data_maps = ext.get::<Vec<SharedDataMap>>();
145
146    if let Some(shared_data_maps) = shared_data_maps {
147        for shared_data_map in shared_data_maps.iter() {
148            if let Some(data) = shared_data_map.inner.get::<T>() {
149                return Some(data);
150            }
151        }
152    }
153
154    None
155}
156
157fn context<T: Send + Sync + Clone + 'static>(ext: &http::Extensions) -> Option<T> {
158    let ctx = ext.get::<RequestContext>().expect("Context must be present");
159    ctx.get::<T>()
160}
161
162fn set_context<T: Send + Sync + Clone + 'static>(ext: &http::Extensions, val: T) {
163    let ctx = ext.get::<RequestContext>().expect("Context must be present");
164    ctx.set(val)
165}
166
167impl RequestExt for Request<hyper::Body> {
168    fn params(&self) -> &RouteParams {
169        params(self.extensions())
170    }
171
172    fn param<P: Into<String>>(&self, param_name: P) -> Option<&String> {
173        param(self.extensions(), param_name)
174    }
175
176    fn remote_addr(&self) -> SocketAddr {
177        remote_addr(self.extensions())
178    }
179
180    fn data<T: Send + Sync + 'static>(&self) -> Option<&T> {
181        data(self.extensions())
182    }
183
184    fn context<T: Send + Sync + Clone + 'static>(&self) -> Option<T> {
185        context(self.extensions())
186    }
187
188    fn set_context<T: Send + Sync + Clone + 'static>(&self, val: T) {
189        set_context(self.extensions(), val)
190    }
191}
192
193impl RequestExt for http::request::Parts {
194    fn params(&self) -> &RouteParams {
195        params(&self.extensions)
196    }
197
198    fn param<P: Into<String>>(&self, param_name: P) -> Option<&String> {
199        param(&self.extensions, param_name)
200    }
201
202    fn remote_addr(&self) -> SocketAddr {
203        remote_addr(&self.extensions)
204    }
205
206    fn data<T: Send + Sync + 'static>(&self) -> Option<&T> {
207        data(&self.extensions)
208    }
209
210    fn context<T: Send + Sync + Clone + 'static>(&self) -> Option<T> {
211        context(&self.extensions)
212    }
213
214    fn set_context<T: Send + Sync + Clone + 'static>(&self, val: T) {
215        set_context(&self.extensions, val)
216    }
217}