volter_core/extract.rs
1//! Extractor traits for request deserialization.
2//!
3//! These traits define how typed values are extracted from incoming HTTP
4//! requests. [`FromRequestParts`] extracts from the request's metadata
5//! (method, URI, headers, extensions) without consuming the body.
6//! [`FromRequest`] may consume the request body.
7
8use std::future::Future;
9use std::pin::Pin;
10
11use crate::body::{BoxBody, Request};
12use crate::into_response::IntoResponse;
13
14/// Extract a typed value from the request's [`http::request::Parts`]
15/// (method, URI, headers, extensions) without consuming the body.
16///
17/// Implement this for anything derivable from headers/URI/method alone —
18/// e.g. `Path<T>`, `Query<T>`, typed headers, `State<T>`.
19pub trait FromRequestParts<S>: Sized {
20 /// The response returned when extraction fails.
21 type Rejection: IntoResponse;
22
23 /// The future returned by [`from_request_parts`].
24 type Future: Future<Output = Result<Self, Self::Rejection>> + Send;
25
26 /// Perform the extraction.
27 fn from_request_parts(parts: &mut http::request::Parts, state: &S) -> Self::Future;
28}
29
30/// Extract a typed value that may need the request body (e.g. `Json<T>`).
31///
32/// Only one `FromRequest` extractor may appear per handler, and it must be
33/// the last argument, since it consumes the body.
34pub trait FromRequest<S, B = BoxBody>: Sized {
35 /// The response returned when extraction fails.
36 type Rejection: IntoResponse;
37
38 /// The future returned by [`from_request`].
39 type Future: Future<Output = Result<Self, Self::Rejection>> + Send;
40
41 /// Perform the extraction, consuming the request.
42 fn from_request(req: http::Request<B>, state: &S) -> Self::Future;
43}
44
45/// Extract typed application state.
46///
47/// `State<T>` extracts a value of type `T` from the router's application
48/// state. The state is cloned on each request — it must implement
49/// [`Clone`].
50///
51/// The extraction always succeeds (the rejection type is [`Infallible`])
52/// because the state type is checked at compile time: a handler that asks
53/// for `State<Foo>` on a router configured with `Bar` will not compile.
54///
55/// # Example
56///
57/// ```rust
58/// use volter_core::{State, Handler};
59///
60/// #[derive(Clone)]
61/// struct AppState { counter: u64 }
62///
63/// async fn handler(State(state): State<AppState>) -> String {
64/// format!("counter: {}", state.counter)
65/// }
66/// ```
67#[derive(Debug, Clone, Copy)]
68pub struct State<T>(pub T);
69
70impl<S: Clone + Send + 'static> FromRequestParts<S> for State<S> {
71 type Rejection = std::convert::Infallible;
72 type Future = std::future::Ready<Result<Self, Self::Rejection>>;
73
74 fn from_request_parts(_parts: &mut http::request::Parts, state: &S) -> Self::Future {
75 std::future::ready(Ok(State(state.clone())))
76 }
77}
78
79// ---------------------------------------------------------------------------
80// FromRequest for State (body-consuming variant)
81// ---------------------------------------------------------------------------
82
83/// [`State`] also implements [`FromRequest`] so it can be the last argument
84/// in a multi-extractor handler tuple. The body is split off and dropped.
85impl<S: Clone + Send + 'static> FromRequest<S, BoxBody> for State<S> {
86 type Rejection = std::convert::Infallible;
87 type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Rejection>> + Send>>;
88
89 fn from_request(req: Request, state: &S) -> Self::Future {
90 let state = state.clone();
91 Box::pin(async move {
92 let (_parts, _body) = req.into_parts();
93 Ok(State(state))
94 })
95 }
96}