1use actix_web::dev::Payload;
57use actix_web::{FromRequest, HttpRequest, HttpResponse, ResponseError};
58use std::fmt;
59use std::future::Future;
60use std::pin::Pin;
61
62#[derive(Debug)]
68pub struct VldJsonError {
69 error: vld::error::VldError,
70}
71
72impl fmt::Display for VldJsonError {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 write!(f, "Validation failed: {}", self.error)
75 }
76}
77
78impl ResponseError for VldJsonError {
79 fn error_response(&self) -> HttpResponse {
80 let errors: Vec<serde_json::Value> = self
81 .error
82 .issues
83 .iter()
84 .map(|issue| {
85 let path: String = issue.path.iter().map(|p| p.to_string()).collect();
86 serde_json::json!({
87 "path": path,
88 "message": issue.message,
89 "code": issue.code.key(),
90 })
91 })
92 .collect();
93
94 let body = serde_json::json!({
95 "error": "Validation failed",
96 "issues": errors,
97 });
98
99 HttpResponse::UnprocessableEntity()
100 .content_type("application/json")
101 .body(body.to_string())
102 }
103}
104
105pub struct VldJson<T>(pub T);
111
112impl<T> std::ops::Deref for VldJson<T> {
113 type Target = T;
114 fn deref(&self) -> &T {
115 &self.0
116 }
117}
118
119impl<T> std::ops::DerefMut for VldJson<T> {
120 fn deref_mut(&mut self) -> &mut T {
121 &mut self.0
122 }
123}
124
125impl<T: vld::schema::VldParse> FromRequest for VldJson<T> {
126 type Error = VldJsonError;
127 type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
128
129 fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
130 let json_fut = actix_web::web::Json::<serde_json::Value>::from_request(req, payload);
131
132 Box::pin(async move {
133 let json_value = json_fut.await.map_err(|e| VldJsonError {
134 error: vld::error::VldError::single(
135 vld::error::IssueCode::ParseError,
136 format!("JSON parse error: {}", e),
137 ),
138 })?;
139
140 let parsed = T::vld_parse_value(&json_value).map_err(|error| VldJsonError { error })?;
141
142 Ok(VldJson(parsed))
143 })
144 }
145}
146
147pub struct VldQuery<T>(pub T);
155
156impl<T> std::ops::Deref for VldQuery<T> {
157 type Target = T;
158 fn deref(&self) -> &T {
159 &self.0
160 }
161}
162
163impl<T> std::ops::DerefMut for VldQuery<T> {
164 fn deref_mut(&mut self) -> &mut T {
165 &mut self.0
166 }
167}
168
169impl<T: vld::schema::VldParse> FromRequest for VldQuery<T> {
170 type Error = VldJsonError;
171 type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
172
173 fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future {
174 let query_string = req.query_string().to_owned();
175
176 Box::pin(async move {
177 let value = query_string_to_json(&query_string);
178
179 let parsed = T::vld_parse_value(&value).map_err(|error| VldJsonError { error })?;
180
181 Ok(VldQuery(parsed))
182 })
183 }
184}
185
186pub struct VldPath<T>(pub T);
211
212impl<T> std::ops::Deref for VldPath<T> {
213 type Target = T;
214 fn deref(&self) -> &T {
215 &self.0
216 }
217}
218
219impl<T> std::ops::DerefMut for VldPath<T> {
220 fn deref_mut(&mut self) -> &mut T {
221 &mut self.0
222 }
223}
224
225impl<T: vld::schema::VldParse> FromRequest for VldPath<T> {
226 type Error = VldJsonError;
227 type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
228
229 fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future {
230 let mut map = serde_json::Map::new();
231
232 if let Some(pattern) = req.match_pattern() {
234 for name in extract_path_param_names(&pattern) {
235 if let Some(value) = req.match_info().get(&name) {
236 map.insert(name, coerce_value(value));
237 }
238 }
239 }
240
241 let value = serde_json::Value::Object(map);
242
243 Box::pin(async move {
244 let parsed = T::vld_parse_value(&value).map_err(|error| VldJsonError { error })?;
245
246 Ok(VldPath(parsed))
247 })
248 }
249}
250
251pub struct VldForm<T>(pub T);
276
277impl<T> std::ops::Deref for VldForm<T> {
278 type Target = T;
279 fn deref(&self) -> &T {
280 &self.0
281 }
282}
283
284impl<T> std::ops::DerefMut for VldForm<T> {
285 fn deref_mut(&mut self) -> &mut T {
286 &mut self.0
287 }
288}
289
290impl<T: vld::schema::VldParse> FromRequest for VldForm<T> {
291 type Error = VldJsonError;
292 type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
293
294 fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
295 let bytes_fut = actix_web::web::Bytes::from_request(req, payload);
296
297 Box::pin(async move {
298 let body = bytes_fut.await.map_err(|e| VldJsonError {
299 error: vld::error::VldError::single(
300 vld::error::IssueCode::ParseError,
301 format!("Failed to read form body: {}", e),
302 ),
303 })?;
304
305 let body_str = std::str::from_utf8(&body).map_err(|_| VldJsonError {
306 error: vld::error::VldError::single(
307 vld::error::IssueCode::ParseError,
308 "Form body is not valid UTF-8",
309 ),
310 })?;
311
312 let value = query_string_to_json(body_str);
313
314 let parsed = T::vld_parse_value(&value).map_err(|error| VldJsonError { error })?;
315
316 Ok(VldForm(parsed))
317 })
318 }
319}
320
321pub struct VldHeaders<T>(pub T);
346
347impl<T> std::ops::Deref for VldHeaders<T> {
348 type Target = T;
349 fn deref(&self) -> &T {
350 &self.0
351 }
352}
353
354impl<T> std::ops::DerefMut for VldHeaders<T> {
355 fn deref_mut(&mut self) -> &mut T {
356 &mut self.0
357 }
358}
359
360impl<T: vld::schema::VldParse> FromRequest for VldHeaders<T> {
361 type Error = VldJsonError;
362 type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
363
364 fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future {
365 let value = headers_to_json(req.headers());
366
367 Box::pin(async move {
368 let parsed = T::vld_parse_value(&value).map_err(|error| VldJsonError { error })?;
369
370 Ok(VldHeaders(parsed))
371 })
372 }
373}
374
375pub struct VldCookie<T>(pub T);
398
399impl<T> std::ops::Deref for VldCookie<T> {
400 type Target = T;
401 fn deref(&self) -> &T {
402 &self.0
403 }
404}
405
406impl<T> std::ops::DerefMut for VldCookie<T> {
407 fn deref_mut(&mut self) -> &mut T {
408 &mut self.0
409 }
410}
411
412impl<T: vld::schema::VldParse> FromRequest for VldCookie<T> {
413 type Error = VldJsonError;
414 type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
415
416 fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future {
417 let cookie_header = req
418 .headers()
419 .get(actix_web::http::header::COOKIE)
420 .and_then(|v| v.to_str().ok())
421 .unwrap_or("")
422 .to_owned();
423
424 Box::pin(async move {
425 let value = cookies_to_json(&cookie_header);
426
427 let parsed = T::vld_parse_value(&value).map_err(|error| VldJsonError { error })?;
428
429 Ok(VldCookie(parsed))
430 })
431 }
432}
433
434use vld_http_common::{
437 coerce_value, cookies_to_json, extract_path_param_names, query_string_to_json,
438};
439
440fn headers_to_json(headers: &actix_web::http::header::HeaderMap) -> serde_json::Value {
444 let mut map = serde_json::Map::new();
445
446 for (name, value) in headers.iter() {
447 let key = name.as_str().replace('-', "_");
448 if let Ok(v) = value.to_str() {
449 map.insert(key, coerce_value(v));
450 }
451 }
452
453 serde_json::Value::Object(map)
454}
455
456pub mod prelude {
458 pub use crate::{VldCookie, VldForm, VldHeaders, VldJson, VldJsonError, VldPath, VldQuery};
459 pub use vld::prelude::*;
460}