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)]
89pub enum ParamError {
90 #[error("Missing required parameter: {0}")]
91 MissingParameter(String),
92
93 #[error("{}", .0.format_error())]
94 InvalidParameter(Box<ParamErrorContext>),
95
96 #[error("{}", .0.format_error())]
97 ParseError(Box<ParamErrorContext>),
98
99 #[error("{}", .0.format_error())]
100 DeserializationError(Box<ParamErrorContext>),
101
102 #[error("{}", .0.format_error())]
103 UrlEncodingError(Box<ParamErrorContext>),
104
105 #[error("Request body error: {0}")]
106 BodyError(String),
107
108 #[error("Payload too large: {0}")]
109 PayloadTooLarge(String),
110
111 #[cfg(feature = "validation")]
112 #[error("{}", .0.format_error())]
113 ValidationError(Box<ParamErrorContext>),
114}
115
116impl ParamError {
117 pub fn json_deserialization<T>(err: serde_json::Error, raw_value: Option<String>) -> Self {
119 let field_name = extract_field_from_serde_error(&err);
120 let mut ctx = ParamErrorContext::new(ParamType::Json, err.to_string())
121 .with_expected_type::<T>()
122 .with_source(Box::new(err));
123
124 if let Some(field) = field_name {
125 ctx = ctx.with_field(field);
126 }
127
128 if let Some(raw) = raw_value {
129 ctx = ctx.with_raw_value(raw);
130 }
131
132 ParamError::DeserializationError(Box::new(ctx))
133 }
134
135 pub fn url_encoding<T>(
137 param_type: ParamType,
138 err: serde_urlencoded::de::Error,
139 raw_value: Option<String>,
140 ) -> Self {
141 let field_name = extract_field_from_urlencoded_error(&err);
142 let mut ctx = ParamErrorContext::new(param_type, err.to_string())
143 .with_expected_type::<T>()
144 .with_source(Box::new(err));
145
146 if let Some(field) = field_name {
147 ctx = ctx.with_field(field);
148 }
149
150 if let Some(raw) = raw_value {
151 ctx = ctx.with_raw_value(raw);
152 }
153
154 ParamError::UrlEncodingError(Box::new(ctx))
155 }
156
157 pub fn invalid<T>(param_type: ParamType, message: impl Into<String>) -> Self {
159 let ctx = ParamErrorContext::new(param_type, message).with_expected_type::<T>();
160 ParamError::InvalidParameter(Box::new(ctx))
161 }
162
163 pub fn parse<T>(
165 param_type: ParamType,
166 message: impl Into<String>,
167 source: Box<dyn std::error::Error + Send + Sync>,
168 ) -> Self {
169 let ctx = ParamErrorContext::new(param_type, message)
170 .with_expected_type::<T>()
171 .with_source(source);
172 ParamError::ParseError(Box::new(ctx))
173 }
174
175 pub fn context(&self) -> Option<&ParamErrorContext> {
177 match self {
178 ParamError::InvalidParameter(ctx) => Some(ctx),
179 ParamError::ParseError(ctx) => Some(ctx),
180 ParamError::DeserializationError(ctx) => Some(ctx),
181 ParamError::UrlEncodingError(ctx) => Some(ctx),
182 #[cfg(feature = "validation")]
183 ParamError::ValidationError(ctx) => Some(ctx),
184 _ => None,
185 }
186 }
187
188 pub fn format_multiline(&self, include_raw_value: bool) -> String {
190 match self.context() {
191 Some(ctx) => ctx.format_multiline(include_raw_value),
192 None => format!(" {}", self),
193 }
194 }
195}
196
197impl From<ParamError> for CoreError {
198 fn from(err: ParamError) -> Self {
199 match err.context() {
201 Some(ctx) => CoreError::ParamValidation(Box::new(ctx.clone())),
202 None => CoreError::Validation(err.to_string()),
203 }
204 }
205}
206
207pub type ParamResult<T> = std::result::Result<T, ParamError>;
208
209pub struct ParamContext {
211 pub path_params: std::collections::HashMap<String, String>,
213 header_names: HashMap<TypeId, &'static str>,
215 cookie_names: HashMap<TypeId, &'static str>,
217}
218
219impl ParamContext {
220 pub fn new() -> Self {
231 Self {
232 path_params: std::collections::HashMap::new(),
233 header_names: HashMap::new(),
234 cookie_names: HashMap::new(),
235 }
236 }
237 pub fn with_path_params(path_params: std::collections::HashMap<String, String>) -> Self {
254 Self {
255 path_params,
256 header_names: HashMap::new(),
257 cookie_names: HashMap::new(),
258 }
259 }
260 pub fn get_path_param(&self, name: &str) -> Option<&str> {
278 self.path_params.get(name).map(|s| s.as_str())
279 }
280
281 pub fn set_header_name<T: 'static>(&mut self, name: &'static str) {
283 self.header_names.insert(TypeId::of::<T>(), name);
284 }
285
286 pub fn get_header_name<T: 'static>(&self) -> Option<&'static str> {
288 self.header_names.get(&TypeId::of::<T>()).copied()
289 }
290
291 pub fn set_cookie_name<T: 'static>(&mut self, name: &'static str) {
293 self.cookie_names.insert(TypeId::of::<T>(), name);
294 }
295
296 pub fn get_cookie_name<T: 'static>(&self) -> Option<&'static str> {
298 self.cookie_names.get(&TypeId::of::<T>()).copied()
299 }
300}
301
302impl Default for ParamContext {
303 fn default() -> Self {
304 Self::new()
305 }
306}