rama_http/service/web/endpoint/extract/
path.rs1use 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
11pub struct Path<T>(pub T);
13
14define_http_rejection! {
15 #[status = INTERNAL_SERVER_ERROR]
16 #[body = "No paths parameters found for matched route"]
17 pub struct MissingPathParams;
19}
20
21composite_http_rejection! {
22 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}