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

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