rama_http/service/web/endpoint/extract/
path.rs

1//! Module in function of the [`Path`] extractor.
2
3use super::FromRequestContextRefPair;
4use crate::dep::http::request::Parts;
5use crate::matcher::{UriParams, UriParamsDeserializeError};
6use crate::utils::macros::{composite_http_rejection, define_http_rejection};
7use rama_core::Context;
8use serde::de::DeserializeOwned;
9use std::ops::{Deref, DerefMut};
10
11/// Extractor to get path parameters from the context in deserialized form.
12pub struct Path<T>(pub T);
13
14define_http_rejection! {
15    #[status = INTERNAL_SERVER_ERROR]
16    #[body = "No paths parameters found for matched route"]
17    /// Rejection type used if rama's internal representation of path parameters is missing.
18    pub struct MissingPathParams;
19}
20
21composite_http_rejection! {
22    /// Rejection used for [`Path`].
23    ///
24    /// Contains one variant for each way the [`Path`](super::Path) extractor
25    /// can fail.
26    pub enum PathRejection {
27        UriParamsDeserializeError,
28        MissingPathParams,
29    }
30}
31
32impl<T: std::fmt::Debug> std::fmt::Debug for Path<T> {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        f.debug_tuple("Path").field(&self.0).finish()
35    }
36}
37
38impl<T: Clone> Clone for Path<T> {
39    fn clone(&self) -> Self {
40        Self(self.0.clone())
41    }
42}
43
44impl<S, T> FromRequestContextRefPair<S> for Path<T>
45where
46    S: Clone + Send + Sync + 'static,
47    T: DeserializeOwned + Send + Sync + 'static,
48{
49    type Rejection = PathRejection;
50
51    async fn from_request_context_ref_pair(
52        ctx: &Context<S>,
53        _parts: &Parts,
54    ) -> Result<Self, Self::Rejection> {
55        match ctx.get::<UriParams>() {
56            Some(params) => {
57                let params = params.deserialize::<T>()?;
58                Ok(Path(params))
59            }
60            None => Err(MissingPathParams.into()),
61        }
62    }
63}
64
65impl<T> Deref for Path<T> {
66    type Target = T;
67
68    fn deref(&self) -> &Self::Target {
69        &self.0
70    }
71}
72
73impl<T> DerefMut for Path<T> {
74    fn deref_mut(&mut self) -> &mut Self::Target {
75        &mut self.0
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    use crate::service::web::WebService;
84    use crate::{Body, Request, StatusCode};
85    use rama_core::Service;
86
87    #[tokio::test]
88    async fn test_host_from_request() {
89        #[derive(Debug, serde::Deserialize)]
90        struct Params {
91            foo: String,
92            bar: u32,
93        }
94
95        let svc =
96            WebService::default().get("/a/:foo/:bar/b/*", async |Path(params): Path<Params>| {
97                assert_eq!(params.foo, "hello");
98                assert_eq!(params.bar, 42);
99                StatusCode::OK
100            });
101
102        let builder = Request::builder()
103            .method("GET")
104            .uri("http://example.com/a/hello/42/b/extra");
105        let req = builder.body(Body::empty()).unwrap();
106
107        let resp = svc.serve(Context::default(), req).await.unwrap();
108        assert_eq!(resp.status(), StatusCode::OK);
109    }
110}