1pub mod body;
40pub mod cookie;
41pub mod cookie_named;
42pub(crate) mod cookie_util;
43pub mod extract;
44pub mod form;
45pub mod has_inner;
46pub mod header;
47pub mod header_named;
48pub mod json;
49#[cfg(feature = "multipart")]
50pub mod multipart;
51pub mod path;
52pub mod query;
53pub mod validation;
54
55use reinhardt_http::Error as CoreError;
56use std::any::TypeId;
57use std::collections::HashMap;
58use thiserror::Error;
59
60pub use reinhardt_core::exception::{ParamErrorContext, ParamType};
62pub use reinhardt_http::Request;
63
64use reinhardt_core::exception::param_error::{
66 extract_field_from_serde_error, extract_field_from_urlencoded_error,
67};
68
69pub use body::Body;
70pub use cookie::{Cookie, CookieStruct};
71pub use cookie_named::{CookieName, CookieNamed, CsrfToken, SessionId};
72pub use extract::FromRequest;
73pub use form::Form;
74pub use has_inner::HasInner;
75pub use header::{Header, HeaderStruct};
76pub use header_named::{Authorization, ContentType, HeaderName, HeaderNamed};
77pub use json::Json;
78#[cfg(feature = "multipart")]
79pub use multipart::Multipart;
80pub use path::{Path, PathStruct};
81pub use query::Query;
82#[cfg(feature = "validation")]
83pub use validation::Validated;
84pub use validation::{
85 ValidatedForm, ValidatedPath, ValidatedQuery, ValidationConstraints, WithValidation,
86};
87
88#[derive(Debug, Error)]
92#[non_exhaustive]
93pub enum ParamError {
94 #[error("Missing required parameter: {0}")]
96 MissingParameter(String),
97
98 #[error("{}", .0.format_error())]
100 InvalidParameter(Box<ParamErrorContext>),
101
102 #[error("{}", .0.format_error())]
104 ParseError(Box<ParamErrorContext>),
105
106 #[error("{}", .0.format_error())]
108 DeserializationError(Box<ParamErrorContext>),
109
110 #[error("{}", .0.format_error())]
112 UrlEncodingError(Box<ParamErrorContext>),
113
114 #[error("Request body error: {0}")]
116 BodyError(String),
117
118 #[error("Payload too large: {0}")]
120 PayloadTooLarge(String),
121
122 #[error("Authentication required: {0}")]
128 Authentication(String),
129
130 #[error("Internal extractor error: {0}")]
136 Internal(String),
137
138 #[cfg(feature = "validation")]
140 #[error("{}", .0.format_error())]
141 ValidationError(Box<ParamErrorContext>),
142
143 #[cfg(feature = "validation")]
150 #[error("Validation failed: {0:?}")]
151 ValidationFailed(Box<reinhardt_core::validators::ValidationErrors>),
152}
153
154impl ParamError {
155 pub fn json_deserialization<T>(err: serde_json::Error, raw_value: Option<String>) -> Self {
157 let field_name = extract_field_from_serde_error(&err);
158 let mut ctx = ParamErrorContext::new(ParamType::Json, err.to_string())
159 .with_expected_type::<T>()
160 .with_source(Box::new(err));
161
162 if let Some(field) = field_name {
163 ctx = ctx.with_field(field);
164 }
165
166 if let Some(raw) = raw_value {
167 ctx = ctx.with_raw_value(raw);
168 }
169
170 ParamError::DeserializationError(Box::new(ctx))
171 }
172
173 pub fn url_encoding<T>(
175 param_type: ParamType,
176 err: serde_urlencoded::de::Error,
177 raw_value: Option<String>,
178 ) -> Self {
179 let field_name = extract_field_from_urlencoded_error(&err);
180 let mut ctx = ParamErrorContext::new(param_type, err.to_string())
181 .with_expected_type::<T>()
182 .with_source(Box::new(err));
183
184 if let Some(field) = field_name {
185 ctx = ctx.with_field(field);
186 }
187
188 if let Some(raw) = raw_value {
189 ctx = ctx.with_raw_value(raw);
190 }
191
192 ParamError::UrlEncodingError(Box::new(ctx))
193 }
194
195 pub fn invalid<T>(param_type: ParamType, message: impl Into<String>) -> Self {
197 let ctx = ParamErrorContext::new(param_type, message).with_expected_type::<T>();
198 ParamError::InvalidParameter(Box::new(ctx))
199 }
200
201 pub fn parse<T>(
203 param_type: ParamType,
204 message: impl Into<String>,
205 source: Box<dyn std::error::Error + Send + Sync>,
206 ) -> Self {
207 let ctx = ParamErrorContext::new(param_type, message)
208 .with_expected_type::<T>()
209 .with_source(source);
210 ParamError::ParseError(Box::new(ctx))
211 }
212
213 pub fn context(&self) -> Option<&ParamErrorContext> {
215 match self {
216 ParamError::InvalidParameter(ctx) => Some(ctx),
217 ParamError::ParseError(ctx) => Some(ctx),
218 ParamError::DeserializationError(ctx) => Some(ctx),
219 ParamError::UrlEncodingError(ctx) => Some(ctx),
220 #[cfg(feature = "validation")]
221 ParamError::ValidationError(ctx) => Some(ctx),
222 _ => None,
223 }
224 }
225
226 pub fn format_multiline(&self, include_raw_value: bool) -> String {
228 match self.context() {
229 Some(ctx) => ctx.format_multiline(include_raw_value),
230 None => format!(" {}", self),
231 }
232 }
233}
234
235impl From<ParamError> for CoreError {
236 fn from(err: ParamError) -> Self {
237 let err = match err {
244 ParamError::Authentication(msg) => return CoreError::Authentication(msg),
245 ParamError::Internal(msg) => return CoreError::Internal(msg),
246 other => other,
247 };
248 match err.context() {
250 Some(ctx) => CoreError::ParamValidation(Box::new(ctx.clone())),
251 None => CoreError::Validation(err.to_string()),
252 }
253 }
254}
255
256pub type ParamResult<T> = std::result::Result<T, ParamError>;
258
259pub struct ParamContext {
261 pub path_params: reinhardt_http::PathParams,
267 header_names: HashMap<TypeId, &'static str>,
269 cookie_names: HashMap<TypeId, &'static str>,
271}
272
273impl ParamContext {
274 pub fn new() -> Self {
285 Self {
286 path_params: reinhardt_http::PathParams::new(),
287 header_names: HashMap::new(),
288 cookie_names: HashMap::new(),
289 }
290 }
291 pub fn with_path_params(path_params: impl Into<reinhardt_http::PathParams>) -> Self {
314 Self {
315 path_params: path_params.into(),
316 header_names: HashMap::new(),
317 cookie_names: HashMap::new(),
318 }
319 }
320 pub fn get_path_param(&self, name: &str) -> Option<&str> {
338 self.path_params.get(name).map(|s| s.as_str())
339 }
340
341 pub fn set_header_name<T: 'static>(&mut self, name: &'static str) {
343 self.header_names.insert(TypeId::of::<T>(), name);
344 }
345
346 pub fn get_header_name<T: 'static>(&self) -> Option<&'static str> {
348 self.header_names.get(&TypeId::of::<T>()).copied()
349 }
350
351 pub fn set_cookie_name<T: 'static>(&mut self, name: &'static str) {
353 self.cookie_names.insert(TypeId::of::<T>(), name);
354 }
355
356 pub fn get_cookie_name<T: 'static>(&self) -> Option<&'static str> {
358 self.cookie_names.get(&TypeId::of::<T>()).copied()
359 }
360}
361
362impl Default for ParamContext {
363 fn default() -> Self {
364 Self::new()
365 }
366}