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}