Skip to main content

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

1//! Module in function of the [`Path`] extractor.
2
3use super::FromPartsStateRefPair;
4use crate::matcher::{UriParams, UriParamsDeserializeError};
5use crate::request::Parts;
6use crate::utils::macros::{composite_http_rejection, define_http_rejection};
7use serde::de::DeserializeOwned;
8use std::ops::{Deref, DerefMut};
9
10/// Extractor to get path parameters from the context in deserialized form.
11#[derive(Debug, Clone)]
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, State> FromPartsStateRefPair<State> for Path<T>
33where
34    T: DeserializeOwned + Send + Sync + 'static,
35    State: Send + Sync,
36{
37    type Rejection = PathRejection;
38
39    async fn from_parts_state_ref_pair(
40        parts: &Parts,
41        _state: &State,
42    ) -> Result<Self, Self::Rejection> {
43        match parts.extensions.get_ref::<UriParams>() {
44            Some(params) => {
45                let params = params.deserialize::<T>()?;
46                Ok(Self(params))
47            }
48            None => Err(MissingPathParams.into()),
49        }
50    }
51}
52
53impl<T> Deref for Path<T> {
54    type Target = T;
55
56    fn deref(&self) -> &Self::Target {
57        &self.0
58    }
59}
60
61impl<T> DerefMut for Path<T> {
62    fn deref_mut(&mut self) -> &mut Self::Target {
63        &mut self.0
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    use crate::service::web::WebService;
72    use crate::{Body, Request, StatusCode};
73    use rama_core::Service;
74
75    #[tokio::test]
76    async fn test_host_from_request() {
77        #[derive(Debug, serde::Deserialize)]
78        struct Params {
79            foo: String,
80            bar: u32,
81        }
82
83        let svc = WebService::default().with_get(
84            "/a/{foo}/{bar}/b/{}",
85            async |Path(params): Path<Params>| {
86                assert_eq!(params.foo, "hello");
87                assert_eq!(params.bar, 42);
88                StatusCode::OK
89            },
90        );
91
92        let builder = Request::builder()
93            .method("GET")
94            .uri("http://example.com/a/hello/42/b/extra");
95        let req = builder.body(Body::empty()).unwrap();
96
97        let resp = svc.serve(req).await.unwrap();
98        assert_eq!(resp.status(), StatusCode::OK);
99    }
100}