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