titan_core/
request.rs

1use crate::Respondable;
2use std::convert::Infallible;
3use titan_http::{Parts, Request, Response};
4
5/// Types that can be extracted from a request.
6///
7/// The `FromRequest` trait is used for types that can be constructed from an HTTP request. Extractors
8/// implementing this trait are responsible for parsing the incoming request body, headers, or other
9/// parts of the request, and converting them into a structured type that can be used by handlers.
10///
11/// Since `FromRequest` extractors consume the request body, they can only be executed once for each
12/// handler. If your extractor doesn't need to consume the request body (for example, when you only
13/// need access to the request headers or parts of the URL), you should implement [`FromRequestParts`]
14/// instead of [`FromRequest`].
15///
16/// # Associated Types
17/// - `Error`: The type of error that can occur while extracting the request data. It must implement
18///   the [`Respondable`] trait to allow the error to be returned as a valid HTTP response.
19///
20/// This method extracts the data from the given HTTP request and returns a result. If the extraction
21/// is successful, it returns `Ok(Self)`, where `Self` is the type implementing `FromRequest`. If
22/// an error occurs during extraction (e.g., due to invalid data or missing fields), it returns an
23/// error of type `Self::Error`, which will be transformed into an HTTP response via the `Respondable`
24/// trait.
25///
26/// # Example
27///
28/// ```
29/// use titan_core::{FromRequest, Respondable};
30/// use titan_http::{Request, body::Body};
31///
32/// // A custom extractor type that implements `FromRequest`.
33/// struct MyExtractor {
34///     pub field: String,
35/// }
36///
37/// // Implement `FromRequest` for `MyExtractor`.
38/// impl FromRequest for MyExtractor {
39///     type Error = String; // The error type we will return if extraction fails.
40///
41///     fn from_request(req: Request) -> Result<Self, Self::Error> {
42///         // Attempt to extract the data from the request (e.g., reading the body).
43///         let field_value = req.uri().path().to_string(); // Example of extracting from the URL path.
44///         Ok(MyExtractor { field: field_value })
45///     }
46/// }
47///
48/// async fn handler(data: MyExtractor) -> impl titan_core::Respondable { /* ... */}
49///
50/// // Now, `MyExtractor` can be used in a handler to extract data from the request.
51/// ```
52pub trait FromRequest: Sized {
53  type Error: Respondable;
54  fn from_request(req: Request) -> Result<Self, Self::Error>;
55}
56
57/// Types that can be created from request parts.
58///
59/// Extractors that implement `FromRequestParts` cannot consume the request body and can thus be
60/// run in any order for handlers.
61///
62/// If your extractor needs to consume the request body then you should implement [`FromRequest`]
63/// and not [`FromRequestParts`].
64pub trait FromRequestParts: Sized {
65  type Error: Respondable;
66  fn from_request_parts(req: &mut Parts) -> Result<Self, Self::Error>;
67}
68
69impl FromRequest for String {
70  type Error = ();
71  fn from_request(req: Request) -> Result<Self, Self::Error> {
72    let test = req.into_body();
73    Ok(String::from_utf8_lossy(&test).to_string())
74  }
75}
76
77impl FromRequest for Request {
78  type Error = Infallible;
79  fn from_request(req: Request) -> Result<Self, Self::Error> {
80    Ok(req)
81  }
82}
83
84impl FromRequest for () {
85  type Error = Infallible;
86  fn from_request(req: Request) -> Result<Self, Self::Error> {
87    let _ = req;
88    Ok(())
89  }
90}
91
92macro_rules! impl_from_request {
93    (
94        [$($ty:ident),*], $last:ident
95    ) => {
96        #[allow(non_snake_case, unused_mut, unused_variables)]
97        impl<$($ty,)* $last> FromRequestParts for ($($ty,)* $last,)
98        where
99            $( $ty: FromRequestParts + Send, )*
100            $last: FromRequestParts + Send,
101        {
102            type Error = Response;
103
104            fn from_request_parts(parts: &mut Parts) -> Result<Self, Self::Error> {
105                $(
106                    let $ty = $ty::from_request_parts(parts)
107                        .map_err(|err| err.respond())?;
108                )*
109                let $last = $last::from_request_parts(parts)
110                    .map_err(|err| err.respond())?;
111
112                Ok(($($ty,)* $last,))
113            }
114        }
115
116        // This impl must not be generic over M, otherwise it would conflict with the blanket
117        // implementation of `FromRequest<S, Mut>` for `T: FromRequestParts<S>`.
118        #[allow(non_snake_case, unused_mut, unused_variables)]
119        impl<$($ty,)* $last> FromRequest for ($($ty,)* $last,)
120        where
121            $( $ty: FromRequestParts + Send, )*
122            $last: FromRequest + Send,
123        {
124            type Error = Response;
125
126            fn from_request(req: Request) -> Result<Self, Self::Error> {
127                let (mut parts, body) = req.into_parts();
128
129                $(
130                    let $ty = $ty::from_request_parts(&mut parts).map_err(|err| err.respond())?;
131                )*
132
133                let req = Request::from_parts(parts, body);
134                let $last = $last::from_request(req).map_err(|err| err.respond())?;
135
136                Ok(($($ty,)* $last,))
137            }
138        }
139    };
140}
141
142all_the_tuples!(impl_from_request);
143
144#[cfg(feature = "deploy-lambda")]
145impl FromRequest for lambda_http::http::Request<lambda_http::Body> {
146  type Error = ();
147  fn from_request(req: Request) -> Result<Self, Self::Error> {
148    let (parts, body) = req.into_parts();
149
150    Ok(lambda_http::http::Request::from_parts(
151      parts,
152      lambda_http::Body::Binary(body.to_vec()),
153    ))
154  }
155}