1pub mod body;
40pub mod cookie;
41pub mod cookie_named;
42pub(crate) mod cookie_util;
43pub mod extract;
44pub mod form;
45pub mod header;
46pub mod header_named;
47pub mod json;
48#[cfg(feature = "multipart")]
49pub mod multipart;
50pub mod path;
51pub mod query;
52pub mod validation;
53
54use reinhardt_http::Error as CoreError;
55use std::any::TypeId;
56use std::collections::HashMap;
57use thiserror::Error;
58
59pub use reinhardt_core::exception::{ParamErrorContext, ParamType};
61pub use reinhardt_http::Request;
62
63use reinhardt_core::exception::param_error::{
65 extract_field_from_serde_error, extract_field_from_urlencoded_error,
66};
67
68pub use body::Body;
69pub use cookie::{Cookie, CookieStruct};
70pub use cookie_named::{CookieName, CookieNamed, CsrfToken, SessionId};
71pub use extract::FromRequest;
72pub use form::Form;
73pub use header::{Header, HeaderStruct};
74pub use header_named::{Authorization, ContentType, HeaderName, HeaderNamed};
75pub use json::Json;
76#[cfg(feature = "multipart")]
77pub use multipart::Multipart;
78pub use path::{Path, PathStruct};
79pub use query::Query;
80#[cfg(feature = "validation")]
81pub use validation::Validated;
82pub use validation::{
83 ValidatedForm, ValidatedPath, ValidatedQuery, ValidationConstraints, WithValidation,
84};
85
86#[derive(Debug, Error)]
90pub enum ParamError {
91 #[error("Missing required parameter: {0}")]
93 MissingParameter(String),
94
95 #[error("{}", .0.format_error())]
97 InvalidParameter(Box<ParamErrorContext>),
98
99 #[error("{}", .0.format_error())]
101 ParseError(Box<ParamErrorContext>),
102
103 #[error("{}", .0.format_error())]
105 DeserializationError(Box<ParamErrorContext>),
106
107 #[error("{}", .0.format_error())]
109 UrlEncodingError(Box<ParamErrorContext>),
110
111 #[error("Request body error: {0}")]
113 BodyError(String),
114
115 #[error("Payload too large: {0}")]
117 PayloadTooLarge(String),
118
119 #[cfg(feature = "validation")]
121 #[error("{}", .0.format_error())]
122 ValidationError(Box<ParamErrorContext>),
123}
124
125impl ParamError {
126 pub fn json_deserialization<T>(err: serde_json::Error, raw_value: Option<String>) -> Self {
128 let field_name = extract_field_from_serde_error(&err);
129 let mut ctx = ParamErrorContext::new(ParamType::Json, err.to_string())
130 .with_expected_type::<T>()
131 .with_source(Box::new(err));
132
133 if let Some(field) = field_name {
134 ctx = ctx.with_field(field);
135 }
136
137 if let Some(raw) = raw_value {
138 ctx = ctx.with_raw_value(raw);
139 }
140
141 ParamError::DeserializationError(Box::new(ctx))
142 }
143
144 pub fn url_encoding<T>(
146 param_type: ParamType,
147 err: serde_urlencoded::de::Error,
148 raw_value: Option<String>,
149 ) -> Self {
150 let field_name = extract_field_from_urlencoded_error(&err);
151 let mut ctx = ParamErrorContext::new(param_type, err.to_string())
152 .with_expected_type::<T>()
153 .with_source(Box::new(err));
154
155 if let Some(field) = field_name {
156 ctx = ctx.with_field(field);
157 }
158
159 if let Some(raw) = raw_value {
160 ctx = ctx.with_raw_value(raw);
161 }
162
163 ParamError::UrlEncodingError(Box::new(ctx))
164 }
165
166 pub fn invalid<T>(param_type: ParamType, message: impl Into<String>) -> Self {
168 let ctx = ParamErrorContext::new(param_type, message).with_expected_type::<T>();
169 ParamError::InvalidParameter(Box::new(ctx))
170 }
171
172 pub fn parse<T>(
174 param_type: ParamType,
175 message: impl Into<String>,
176 source: Box<dyn std::error::Error + Send + Sync>,
177 ) -> Self {
178 let ctx = ParamErrorContext::new(param_type, message)
179 .with_expected_type::<T>()
180 .with_source(source);
181 ParamError::ParseError(Box::new(ctx))
182 }
183
184 pub fn context(&self) -> Option<&ParamErrorContext> {
186 match self {
187 ParamError::InvalidParameter(ctx) => Some(ctx),
188 ParamError::ParseError(ctx) => Some(ctx),
189 ParamError::DeserializationError(ctx) => Some(ctx),
190 ParamError::UrlEncodingError(ctx) => Some(ctx),
191 #[cfg(feature = "validation")]
192 ParamError::ValidationError(ctx) => Some(ctx),
193 _ => None,
194 }
195 }
196
197 pub fn format_multiline(&self, include_raw_value: bool) -> String {
199 match self.context() {
200 Some(ctx) => ctx.format_multiline(include_raw_value),
201 None => format!(" {}", self),
202 }
203 }
204}
205
206impl From<ParamError> for CoreError {
207 fn from(err: ParamError) -> Self {
208 match err.context() {
210 Some(ctx) => CoreError::ParamValidation(Box::new(ctx.clone())),
211 None => CoreError::Validation(err.to_string()),
212 }
213 }
214}
215
216pub type ParamResult<T> = std::result::Result<T, ParamError>;
218
219pub struct ParamContext {
221 pub path_params: std::collections::HashMap<String, String>,
223 header_names: HashMap<TypeId, &'static str>,
225 cookie_names: HashMap<TypeId, &'static str>,
227}
228
229impl ParamContext {
230 pub fn new() -> Self {
241 Self {
242 path_params: std::collections::HashMap::new(),
243 header_names: HashMap::new(),
244 cookie_names: HashMap::new(),
245 }
246 }
247 pub fn with_path_params(path_params: std::collections::HashMap<String, String>) -> Self {
264 Self {
265 path_params,
266 header_names: HashMap::new(),
267 cookie_names: HashMap::new(),
268 }
269 }
270 pub fn get_path_param(&self, name: &str) -> Option<&str> {
288 self.path_params.get(name).map(|s| s.as_str())
289 }
290
291 pub fn set_header_name<T: 'static>(&mut self, name: &'static str) {
293 self.header_names.insert(TypeId::of::<T>(), name);
294 }
295
296 pub fn get_header_name<T: 'static>(&self) -> Option<&'static str> {
298 self.header_names.get(&TypeId::of::<T>()).copied()
299 }
300
301 pub fn set_cookie_name<T: 'static>(&mut self, name: &'static str) {
303 self.cookie_names.insert(TypeId::of::<T>(), name);
304 }
305
306 pub fn get_cookie_name<T: 'static>(&self) -> Option<&'static str> {
308 self.cookie_names.get(&TypeId::of::<T>()).copied()
309 }
310}
311
312impl Default for ParamContext {
313 fn default() -> Self {
314 Self::new()
315 }
316}