1use std::collections::HashMap;
30use std::convert::Infallible;
31use std::future::Future;
32use std::pin::Pin;
33use crate::{Request, Response};
34use http::{HeaderMap, StatusCode};
35
36pub trait FromRequest: Sized {
38 type Error: IntoResponse + Send + Sync;
40
41 fn from_request(
43 req: Request,
44 ) -> Pin<Box<dyn Future<Output = Result<(Self, Request), Self::Error>> + Send + 'static>>;
45}
46
47pub trait FromRequestParts: Sized {
49 type Error: IntoResponse + Send + Sync;
51
52 fn from_request_parts(
54 req: &mut Request,
55 ) -> Pin<Box<dyn Future<Output = Result<Self, Self::Error>> + Send + 'static>>;
56}
57
58#[derive(Debug)]
60pub enum ExtractionError {
61 MissingPathParam(String),
63 InvalidPathParam(String),
65 InvalidQuery(String),
67 InvalidJson(String),
69 MissingHeader(String),
71 InvalidHeader(String),
73 MissingState(String),
75 InvalidForm(String),
77 InvalidCookie(String),
79 ContentTooLarge(String),
81 UnsupportedMediaType(String),
83 Custom(String),
85}
86
87impl std::fmt::Display for ExtractionError {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 match self {
90 ExtractionError::MissingPathParam(msg) => write!(f, "Missing path parameter: {}", msg),
91 ExtractionError::InvalidPathParam(msg) => write!(f, "Invalid path parameter: {}", msg),
92 ExtractionError::InvalidQuery(msg) => write!(f, "Invalid query parameter: {}", msg),
93 ExtractionError::InvalidJson(msg) => write!(f, "Invalid JSON body: {}", msg),
94 ExtractionError::MissingHeader(msg) => write!(f, "Missing header: {}", msg),
95 ExtractionError::InvalidHeader(msg) => write!(f, "Invalid header: {}", msg),
96 ExtractionError::MissingState(msg) => write!(f, "Missing application state: {}", msg),
97 ExtractionError::InvalidForm(msg) => write!(f, "Invalid form data: {}", msg),
98 ExtractionError::InvalidCookie(msg) => write!(f, "Invalid cookie: {}", msg),
99 ExtractionError::ContentTooLarge(msg) => write!(f, "Content too large: {}", msg),
100 ExtractionError::UnsupportedMediaType(msg) => write!(f, "Unsupported media type: {}", msg),
101 ExtractionError::Custom(msg) => write!(f, "{}", msg),
102 }
103 }
104}
105
106impl std::error::Error for ExtractionError {}
107
108unsafe impl Send for ExtractionError {}
110unsafe impl Sync for ExtractionError {}
111
112pub trait IntoResponse {
114 fn into_response(self) -> Response;
115}
116
117impl IntoResponse for ExtractionError {
118 fn into_response(self) -> Response {
119 let status = match self {
120 ExtractionError::MissingPathParam(_) | ExtractionError::InvalidPathParam(_) => {
121 StatusCode::BAD_REQUEST
122 }
123 ExtractionError::InvalidQuery(_) => StatusCode::BAD_REQUEST,
124 ExtractionError::InvalidJson(_) => StatusCode::BAD_REQUEST,
125 ExtractionError::MissingHeader(_) | ExtractionError::InvalidHeader(_) => {
126 StatusCode::BAD_REQUEST
127 }
128 ExtractionError::MissingState(_) => StatusCode::INTERNAL_SERVER_ERROR,
129 ExtractionError::InvalidForm(_) => StatusCode::BAD_REQUEST,
130 ExtractionError::InvalidCookie(_) => StatusCode::BAD_REQUEST,
131 ExtractionError::ContentTooLarge(_) => StatusCode::PAYLOAD_TOO_LARGE,
132 ExtractionError::UnsupportedMediaType(_) => StatusCode::UNSUPPORTED_MEDIA_TYPE,
133 ExtractionError::Custom(_) => StatusCode::BAD_REQUEST,
134 };
135
136 Response::with_status(status).body(self.to_string())
137 }
138}
139
140impl IntoResponse for Infallible {
141 fn into_response(self) -> Response {
142 match self {}
143 }
144}
145
146impl IntoResponse for Response {
147 fn into_response(self) -> Response {
148 self
149 }
150}
151
152impl IntoResponse for &'static str {
153 fn into_response(self) -> Response {
154 Response::ok().body(self)
155 }
156}
157
158impl IntoResponse for String {
159 fn into_response(self) -> Response {
160 Response::ok().body(self)
161 }
162}
163
164impl IntoResponse for StatusCode {
165 fn into_response(self) -> Response {
166 Response::with_status(self)
167 }
168}
169
170impl<T> IntoResponse for (StatusCode, T)
171where
172 T: IntoResponse,
173{
174 fn into_response(self) -> Response {
175 let mut response = self.1.into_response();
176 *response.status_code_mut() = self.0;
177 response
178 }
179}
180
181pub use path::Path;
183pub use query::{Query, SerdeQuery};
184pub use headers::Headers;
185pub use state::State;
186pub use form::{Form, SerdeForm};
187pub use cookies::{Cookies, SessionCookie, CookieBuilder, SameSite, get_cookie, get_required_cookie};
188
189#[cfg(feature = "json")]
190pub use json::Json;
191
192mod path;
194mod query;
195mod headers;
196pub mod state;
197mod form;
198mod cookies;
199
200#[cfg(feature = "json")]
201mod json;