valico/json_schema/validators/
formats.rs1use addr::parser::{DomainName, EmailAddress};
2use addr::psl::List;
3use chrono;
4use json_pointer;
5use serde_json::Value;
6use std::net;
7use uritemplate;
8use url;
9use uuid;
10
11use super::super::errors;
12use super::super::scope;
13
14#[allow(missing_copy_implementations)]
15pub struct Date;
16
17impl super::Validator for Date {
18 fn validate(
19 &self,
20 val: &Value,
21 path: &str,
22 _scope: &scope::Scope,
23 _: &super::ValidationState,
24 ) -> super::ValidationState {
25 let string = nonstrict_process!(val.as_str(), path);
26
27 match chrono::NaiveDate::parse_from_str(string, "%Y-%m-%d") {
28 Ok(_) => {
29 if string.len() == 10 {
30 super::ValidationState::new()
31 } else {
32 val_error!(errors::Format {
33 path: path.to_string(),
34 detail: "Malformed Date".to_string()
35 })
36 }
37 }
38 Err(_) => val_error!(errors::Format {
39 path: path.to_string(),
40 detail: "Malformed date".to_string()
41 }),
42 }
43 }
44}
45
46#[allow(missing_copy_implementations)]
47pub struct DateTime;
48
49impl super::Validator for DateTime {
50 fn validate(
51 &self,
52 val: &Value,
53 path: &str,
54 _scope: &scope::Scope,
55 _: &super::ValidationState,
56 ) -> super::ValidationState {
57 let string = nonstrict_process!(val.as_str(), path);
58
59 match chrono::DateTime::parse_from_rfc3339(string) {
60 Ok(_) => super::ValidationState::new(),
61 Err(_) => val_error!(errors::Format {
62 path: path.to_string(),
63 detail: "Malformed date time".to_string()
64 }),
65 }
66 }
67}
68
69#[allow(missing_copy_implementations)]
70pub struct Email;
71
72impl super::Validator for Email {
73 fn validate(
74 &self,
75 val: &Value,
76 path: &str,
77 _scope: &scope::Scope,
78 _: &super::ValidationState,
79 ) -> super::ValidationState {
80 let string = nonstrict_process!(val.as_str(), path);
81
82 match List.parse_email_address(string) {
83 Ok(_) => super::ValidationState::new(),
84 Err(_) => val_error!(errors::Format {
85 path: path.to_string(),
86 detail: "Malformed email address".to_string()
87 }),
88 }
89 }
90}
91
92#[allow(missing_copy_implementations)]
93pub struct Hostname;
94
95impl super::Validator for Hostname {
96 fn validate(
97 &self,
98 val: &Value,
99 path: &str,
100 _scope: &scope::Scope,
101 _: &super::ValidationState,
102 ) -> super::ValidationState {
103 let string = nonstrict_process!(val.as_str(), path);
104
105 match List.parse_domain_name(string) {
106 Ok(_) => super::ValidationState::new(),
107 Err(_) => val_error!(errors::Format {
108 path: path.to_string(),
109 detail: "Malformed hostname".to_string()
110 }),
111 }
112 }
113}
114
115#[allow(missing_copy_implementations)]
116pub struct Ipv4;
117
118impl super::Validator for Ipv4 {
119 fn validate(
120 &self,
121 val: &Value,
122 path: &str,
123 _scope: &scope::Scope,
124 _: &super::ValidationState,
125 ) -> super::ValidationState {
126 let string = nonstrict_process!(val.as_str(), path);
127
128 match string.parse::<net::Ipv4Addr>() {
129 Ok(_) => super::ValidationState::new(),
130 Err(_) => val_error!(errors::Format {
131 path: path.to_string(),
132 detail: "Malformed IP address".to_string()
133 }),
134 }
135 }
136}
137
138#[allow(missing_copy_implementations)]
139pub struct Ipv6;
140
141impl super::Validator for Ipv6 {
142 fn validate(
143 &self,
144 val: &Value,
145 path: &str,
146 _scope: &scope::Scope,
147 _: &super::ValidationState,
148 ) -> super::ValidationState {
149 let string = nonstrict_process!(val.as_str(), path);
150
151 match string.parse::<net::Ipv6Addr>() {
152 Ok(_) => super::ValidationState::new(),
153 Err(_) => val_error!(errors::Format {
154 path: path.to_string(),
155 detail: "Malformed IP address".to_string()
156 }),
157 }
158 }
159}
160
161#[allow(missing_copy_implementations)]
162pub struct IRI;
163
164impl super::Validator for IRI {
165 fn validate(
166 &self,
167 val: &Value,
168 path: &str,
169 _scope: &scope::Scope,
170 _: &super::ValidationState,
171 ) -> super::ValidationState {
172 let string = nonstrict_process!(val.as_str(), path);
173
174 match url::Url::parse(string) {
175 Ok(_) => super::ValidationState::new(),
176 Err(err) => val_error!(errors::Format {
177 path: path.to_string(),
178 detail: format!("Malformed IRI: {err}")
179 }),
180 }
181 }
182}
183
184#[allow(missing_copy_implementations)]
185pub struct IRIReference;
186
187impl super::Validator for IRIReference {
188 fn validate(
189 &self,
190 val: &Value,
191 path: &str,
192 _scope: &scope::Scope,
193 _: &super::ValidationState,
194 ) -> super::ValidationState {
195 let string = nonstrict_process!(val.as_str(), path);
196
197 let base_url = url::Url::parse("http://example.com/").unwrap();
198
199 match base_url.join(string) {
200 Ok(_) => super::ValidationState::new(),
201 Err(err) => val_error!(errors::Format {
202 path: path.to_string(),
203 detail: format!("Malformed IRI reference: {err}")
204 }),
205 }
206 }
207}
208
209#[allow(missing_copy_implementations)]
210pub struct JsonPointer;
211
212impl super::Validator for JsonPointer {
213 fn validate(
214 &self,
215 val: &Value,
216 path: &str,
217 _scope: &scope::Scope,
218 _: &super::ValidationState,
219 ) -> super::ValidationState {
220 let string = nonstrict_process!(val.as_str(), path);
221
222 match string.parse::<json_pointer::JsonPointer<_, _>>() {
223 Ok(_) => super::ValidationState::new(),
224 Err(_) => val_error!(errors::Format {
225 path: path.to_string(),
226 detail: "Malformed JSON pointer".to_string()
227 }),
228 }
229 }
230}
231
232#[allow(missing_copy_implementations)]
233pub struct Regex;
234
235impl super::Validator for Regex {
236 fn validate(
237 &self,
238 val: &Value,
239 path: &str,
240 _scope: &scope::Scope,
241 _: &super::ValidationState,
242 ) -> super::ValidationState {
243 let string = nonstrict_process!(val.as_str(), path);
244
245 let string = string.replace(r"\/", "/");
250
251 match fancy_regex::Regex::new(&string) {
252 Ok(_) => super::ValidationState::new(),
253 Err(er) => {
254 val_error!(errors::Format {
255 path: path.to_string(),
256 detail: format!("Malformed regex - {er}")
257 })
258 }
259 }
260 }
261}
262
263#[allow(missing_copy_implementations)]
264pub struct RelativeJsonPointer;
265
266impl super::Validator for RelativeJsonPointer {
267 fn validate(
268 &self,
269 val: &Value,
270 path: &str,
271 _scope: &scope::Scope,
272 _: &super::ValidationState,
273 ) -> super::ValidationState {
274 let string = nonstrict_process!(val.as_str(), path);
275
276 match string.parse::<json_pointer::JsonPointer<_, _>>() {
277 Ok(_) => super::ValidationState::new(),
278 Err(_) => val_error!(errors::Format {
279 path: path.to_string(),
280 detail: "Malformed relative JSON pointer".to_string()
281 }),
282 }
283 }
284}
285
286#[allow(missing_copy_implementations)]
287pub struct Time;
288
289impl super::Validator for Time {
290 fn validate(
291 &self,
292 val: &Value,
293 path: &str,
294 _scope: &scope::Scope,
295 _: &super::ValidationState,
296 ) -> super::ValidationState {
297 let string = nonstrict_process!(val.as_str(), path);
298
299 match chrono::NaiveTime::parse_from_str(string, "%H:%M:%S%.f") {
300 Ok(_) => super::ValidationState::new(),
301 Err(_) => val_error!(errors::Format {
302 path: path.to_string(),
303 detail: "Malformed time".to_string()
304 }),
305 }
306 }
307}
308
309#[allow(missing_copy_implementations)]
310pub struct Uuid;
311
312impl super::Validator for Uuid {
313 fn validate(
314 &self,
315 val: &Value,
316 path: &str,
317 _scope: &scope::Scope,
318 _: &super::ValidationState,
319 ) -> super::ValidationState {
320 let string = nonstrict_process!(val.as_str(), path);
321
322 match string.parse::<uuid::Uuid>() {
323 Ok(_) => super::ValidationState::new(),
324 Err(err) => val_error!(errors::Format {
325 path: path.to_string(),
326 detail: format!("Malformed UUID: {err:?}")
327 }),
328 }
329 }
330}
331
332#[allow(missing_copy_implementations)]
333pub struct Uri;
334
335impl super::Validator for Uri {
336 fn validate(
337 &self,
338 val: &Value,
339 path: &str,
340 _scope: &scope::Scope,
341 _: &super::ValidationState,
342 ) -> super::ValidationState {
343 let string = nonstrict_process!(val.as_str(), path);
344
345 match url::Url::parse(string) {
346 Ok(_) => super::ValidationState::new(),
347 Err(err) => val_error!(errors::Format {
348 path: path.to_string(),
349 detail: format!("Malformed URI: {err}")
350 }),
351 }
352 }
353}
354
355#[allow(missing_copy_implementations)]
356pub struct UriReference;
357
358impl super::Validator for UriReference {
359 fn validate(
360 &self,
361 val: &Value,
362 path: &str,
363 _scope: &scope::Scope,
364 _: &super::ValidationState,
365 ) -> super::ValidationState {
366 let string = nonstrict_process!(val.as_str(), path);
367
368 let base_url = url::Url::parse("http://example.com/").unwrap();
369
370 match base_url.join(string) {
371 Ok(_) => super::ValidationState::new(),
372 Err(err) => val_error!(errors::Format {
373 path: path.to_string(),
374 detail: format!("Malformed URI reference: {err}")
375 }),
376 }
377 }
378}
379
380#[allow(missing_copy_implementations)]
381pub struct UriTemplate;
382
383impl super::Validator for UriTemplate {
384 fn validate(
385 &self,
386 val: &Value,
387 _path: &str,
388 _scope: &scope::Scope,
389 _: &super::ValidationState,
390 ) -> super::ValidationState {
391 let string = nonstrict_process!(val.as_str(), path);
392
393 let _ = uritemplate::UriTemplate::new(string);
394 super::ValidationState::new()
395 }
396}
397
398#[cfg(test)]
399pub mod tests {
400 use super::Regex;
401 use crate::json_schema::validators::Validator;
402 use crate::json_schema::{Scope, ValidationState};
403
404 #[test]
405 fn validate_valid_empty_regex() {
406 let result = validate_regex("");
407 assert!(result.errors.is_empty())
408 }
409
410 #[test]
411 fn validate_valid_regex_simple() {
412 let result = validate_regex("^[a-z][a-z0-9]{0,10}$");
413 assert!(result.errors.is_empty())
414 }
415
416 #[test]
417 fn validate_valid_regex_with_double_escaped_forward_slash() {
418 let result = validate_regex("\\w+:(\\/?\\/?)[^\\s]+");
419 assert!(result.errors.is_empty())
420 }
421
422 #[test]
423 fn validate_invalid_regex() {
424 let result = validate_regex("FOO\\");
425 assert_eq!(result.errors.len(), 1);
426
427 let only_err = result.errors.get(0);
428 assert!(only_err.is_some());
429
430 let err = only_err.unwrap();
431 assert!(err.get_detail().is_some());
432 assert!(err.get_detail().unwrap().contains("Malformed regex"))
433 }
434
435 fn validate_regex(json_string: &str) -> ValidationState {
436 let value = serde_json::value::Value::String(json_string.into());
437 let scope = Scope::new();
438 Regex {}.validate(&value, "/", &scope, &Default::default())
439 }
440}