1use ntex::http::StatusCode;
46use ntex::web::error::WebResponseError;
47use ntex::web::{ErrorRenderer, FromRequest, HttpRequest, HttpResponse};
48use std::fmt;
49
50#[derive(Debug)]
56pub struct VldNtexError {
57 error: vld::error::VldError,
58}
59
60impl fmt::Display for VldNtexError {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 write!(f, "Validation failed: {}", self.error)
63 }
64}
65
66impl<Err: ErrorRenderer> WebResponseError<Err> for VldNtexError {
67 fn status_code(&self) -> StatusCode {
68 StatusCode::UNPROCESSABLE_ENTITY
69 }
70
71 fn error_response(&self, _: &HttpRequest) -> HttpResponse {
72 let body = vld_http_common::format_vld_error(&self.error);
73
74 HttpResponse::build(StatusCode::UNPROCESSABLE_ENTITY)
75 .header("content-type", "application/json")
76 .body(body.to_string())
77 }
78}
79
80pub struct VldJson<T>(pub T);
86
87impl<T> std::ops::Deref for VldJson<T> {
88 type Target = T;
89 fn deref(&self) -> &T {
90 &self.0
91 }
92}
93
94impl<T> std::ops::DerefMut for VldJson<T> {
95 fn deref_mut(&mut self) -> &mut T {
96 &mut self.0
97 }
98}
99
100impl<T: vld::schema::VldParse, Err: ErrorRenderer> FromRequest<Err> for VldJson<T> {
101 type Error = VldNtexError;
102
103 async fn from_request(
104 req: &HttpRequest,
105 payload: &mut ntex::http::Payload,
106 ) -> Result<Self, Self::Error> {
107 let json_value =
108 <ntex::web::types::Json<serde_json::Value> as FromRequest<Err>>::from_request(
109 req, payload,
110 )
111 .await
112 .map_err(|e| VldNtexError {
113 error: vld::error::VldError::single(
114 vld::error::IssueCode::ParseError,
115 format!("JSON parse error: {}", e),
116 ),
117 })?;
118
119 let parsed = T::vld_parse_value(&json_value).map_err(|error| VldNtexError { error })?;
120
121 Ok(VldJson(parsed))
122 }
123}
124
125pub struct VldQuery<T>(pub T);
133
134impl<T> std::ops::Deref for VldQuery<T> {
135 type Target = T;
136 fn deref(&self) -> &T {
137 &self.0
138 }
139}
140
141impl<T> std::ops::DerefMut for VldQuery<T> {
142 fn deref_mut(&mut self) -> &mut T {
143 &mut self.0
144 }
145}
146
147impl<T: vld::schema::VldParse, Err: ErrorRenderer> FromRequest<Err> for VldQuery<T> {
148 type Error = VldNtexError;
149
150 async fn from_request(
151 req: &HttpRequest,
152 _payload: &mut ntex::http::Payload,
153 ) -> Result<Self, Self::Error> {
154 let query_string = req.query_string();
155 let value = query_string_to_json(query_string);
156 let parsed = T::vld_parse_value(&value).map_err(|error| VldNtexError { error })?;
157 Ok(VldQuery(parsed))
158 }
159}
160
161pub struct VldPath<T>(pub T);
169
170impl<T> std::ops::Deref for VldPath<T> {
171 type Target = T;
172 fn deref(&self) -> &T {
173 &self.0
174 }
175}
176
177impl<T> std::ops::DerefMut for VldPath<T> {
178 fn deref_mut(&mut self) -> &mut T {
179 &mut self.0
180 }
181}
182
183impl<T: vld::schema::VldParse, Err: ErrorRenderer> FromRequest<Err> for VldPath<T> {
184 type Error = VldNtexError;
185
186 async fn from_request(
187 req: &HttpRequest,
188 _payload: &mut ntex::http::Payload,
189 ) -> Result<Self, Self::Error> {
190 let mut map = serde_json::Map::new();
191 for (name, value) in req.match_info().iter() {
192 map.insert(name.to_owned(), coerce_value(value));
193 }
194
195 let value = serde_json::Value::Object(map);
196 let parsed = T::vld_parse_value(&value).map_err(|error| VldNtexError { error })?;
197 Ok(VldPath(parsed))
198 }
199}
200
201pub struct VldForm<T>(pub T);
210
211impl<T> std::ops::Deref for VldForm<T> {
212 type Target = T;
213 fn deref(&self) -> &T {
214 &self.0
215 }
216}
217
218impl<T> std::ops::DerefMut for VldForm<T> {
219 fn deref_mut(&mut self) -> &mut T {
220 &mut self.0
221 }
222}
223
224impl<T: vld::schema::VldParse, Err: ErrorRenderer> FromRequest<Err> for VldForm<T> {
225 type Error = VldNtexError;
226
227 async fn from_request(
228 req: &HttpRequest,
229 payload: &mut ntex::http::Payload,
230 ) -> Result<Self, Self::Error> {
231 let bytes = <ntex::util::Bytes as FromRequest<Err>>::from_request(req, payload)
232 .await
233 .map_err(|e| VldNtexError {
234 error: vld::error::VldError::single(
235 vld::error::IssueCode::ParseError,
236 format!("Failed to read form body: {}", e),
237 ),
238 })?;
239
240 let body_bytes: &[u8] = &bytes;
241 let body_str = std::str::from_utf8(body_bytes).map_err(|_| VldNtexError {
242 error: vld::error::VldError::single(
243 vld::error::IssueCode::ParseError,
244 "Form body is not valid UTF-8",
245 ),
246 })?;
247
248 let value = query_string_to_json(body_str);
249 let parsed = T::vld_parse_value(&value).map_err(|error| VldNtexError { error })?;
250 Ok(VldForm(parsed))
251 }
252}
253
254pub struct VldHeaders<T>(pub T);
263
264impl<T> std::ops::Deref for VldHeaders<T> {
265 type Target = T;
266 fn deref(&self) -> &T {
267 &self.0
268 }
269}
270
271impl<T> std::ops::DerefMut for VldHeaders<T> {
272 fn deref_mut(&mut self) -> &mut T {
273 &mut self.0
274 }
275}
276
277impl<T: vld::schema::VldParse, Err: ErrorRenderer> FromRequest<Err> for VldHeaders<T> {
278 type Error = VldNtexError;
279
280 async fn from_request(
281 req: &HttpRequest,
282 _payload: &mut ntex::http::Payload,
283 ) -> Result<Self, Self::Error> {
284 let value = headers_to_json(req.headers());
285 let parsed = T::vld_parse_value(&value).map_err(|error| VldNtexError { error })?;
286 Ok(VldHeaders(parsed))
287 }
288}
289
290pub struct VldCookie<T>(pub T);
297
298impl<T> std::ops::Deref for VldCookie<T> {
299 type Target = T;
300 fn deref(&self) -> &T {
301 &self.0
302 }
303}
304
305impl<T> std::ops::DerefMut for VldCookie<T> {
306 fn deref_mut(&mut self) -> &mut T {
307 &mut self.0
308 }
309}
310
311impl<T: vld::schema::VldParse, Err: ErrorRenderer> FromRequest<Err> for VldCookie<T> {
312 type Error = VldNtexError;
313
314 async fn from_request(
315 req: &HttpRequest,
316 _payload: &mut ntex::http::Payload,
317 ) -> Result<Self, Self::Error> {
318 let cookie_header = req
319 .headers()
320 .get(ntex::http::header::COOKIE)
321 .and_then(|v| v.to_str().ok())
322 .unwrap_or("");
323
324 let value = cookies_to_json(cookie_header);
325 let parsed = T::vld_parse_value(&value).map_err(|error| VldNtexError { error })?;
326 Ok(VldCookie(parsed))
327 }
328}
329
330use vld_http_common::{coerce_value, cookies_to_json, query_string_to_json};
333
334fn headers_to_json(headers: &ntex::http::HeaderMap) -> serde_json::Value {
335 let mut map = serde_json::Map::new();
336
337 for (name, value) in headers.iter() {
338 let key = name.as_str().replace('-', "_");
339 if let Ok(v) = value.to_str() {
340 map.insert(key, coerce_value(v));
341 }
342 }
343
344 serde_json::Value::Object(map)
345}
346
347pub mod prelude {
349 pub use crate::{VldCookie, VldForm, VldHeaders, VldJson, VldNtexError, VldPath, VldQuery};
350 pub use vld::prelude::*;
351}