valico/json_schema/keywords/
format.rs

1use serde_json::{Value};
2use std::collections;
3
4use super::super::schema;
5use super::super::validators;
6
7pub type FormatBuilders = collections::HashMap<String, Box<super::Keyword + Send + Sync>>;
8
9fn default_formats() -> FormatBuilders {
10    let mut map: FormatBuilders = collections::HashMap::new();
11
12    let date_time_builder = Box::new(|_def: &Value, _ctx: &schema::WalkContext| {
13        Ok(Some(Box::new(validators::formats::DateTime) as validators::BoxedValidator))
14    });
15    map.insert("date-time".to_string(), date_time_builder);
16
17    let email_builder = Box::new(|_def: &Value, _ctx: &schema::WalkContext| {
18        Ok(Some(Box::new(validators::formats::Email) as validators::BoxedValidator))
19    });
20    map.insert("email".to_string(), email_builder);
21
22    let hostname_builder = Box::new(|_def: &Value, _ctx: &schema::WalkContext| {
23        Ok(Some(Box::new(validators::formats::Hostname) as validators::BoxedValidator))
24    });
25    map.insert("hostname".to_string(), hostname_builder);
26
27    let ipv4_builder = Box::new(|_def: &Value, _ctx: &schema::WalkContext| {
28        Ok(Some(Box::new(validators::formats::Ipv4) as validators::BoxedValidator))
29    });
30    map.insert("ipv4".to_string(), ipv4_builder);
31
32    let ipv6_builder = Box::new(|_def: &Value, _ctx: &schema::WalkContext| {
33        Ok(Some(Box::new(validators::formats::Ipv6) as validators::BoxedValidator))
34    });
35    map.insert("ipv6".to_string(), ipv6_builder);
36
37    let uri_builder = Box::new(|_def: &Value, _ctx: &schema::WalkContext| {
38        Ok(Some(Box::new(validators::formats::Uri) as validators::BoxedValidator))
39    });
40    map.insert("uri".to_string(), uri_builder);
41
42    let uuid_builder = Box::new(|_def: &Value, _ctx: &schema::WalkContext| {
43        Ok(Some(Box::new(validators::formats::Uuid) as validators::BoxedValidator))
44    });
45    map.insert("uuid".to_string(), uuid_builder);
46
47    map
48}
49
50#[allow(missing_copy_implementations)]
51pub struct Format {
52    pub formats: FormatBuilders
53}
54
55impl Format {
56    pub fn new() -> Format {
57        Format {
58            formats: default_formats()
59        }
60    }
61
62    pub fn with<F>(build_formats: F) -> Format where F: FnOnce(&mut FormatBuilders) {
63        let mut formats = default_formats();
64        build_formats(&mut formats);
65        Format {
66            formats: formats
67        }
68    }
69}
70
71impl super::Keyword for Format {
72    fn compile(&self, def: &Value, ctx: &schema::WalkContext) -> super::KeywordResult {
73        let format = keyword_key_exists!(def, "format");
74
75        if format.is_string() {
76            let format = format.as_str().unwrap();
77            match self.formats.get(format) {
78                Some(keyword) => {
79                    keyword.compile(def, ctx)
80                },
81                None => {
82                    Ok(None)
83                }
84            }
85        } else {
86            Err(schema::SchemaError::Malformed {
87                path: ctx.fragment.join("/"),
88                detail: "The value of format MUST be a string".to_string()
89            })
90        }
91    }
92}
93
94#[cfg(test)] use super::super::scope;
95#[cfg(test)] use super::super::builder;
96#[cfg(test)] use serde_json::to_value;
97
98#[test]
99fn validate_date_time() {
100    let mut scope = scope::Scope::new();
101    let schema = scope.compile_and_return(builder::schema(|s| {
102        s.format("date-time");
103    }).into_json(), true).ok().unwrap();
104
105    assert_eq!(schema.validate(&to_value(&"2015-01-20T17:35:20-08:00").unwrap()).is_valid(), true);
106    assert_eq!(schema.validate(&to_value(&"1944-06-06T04:04:00Z").unwrap()).is_valid(), true);
107    assert_eq!(schema.validate(&to_value(&"Tue, 20 Jan 2015 17:35:20 -0800").unwrap()).is_valid(), false);
108}
109
110#[test]
111fn validate_email() {
112    let mut scope = scope::Scope::new();
113    let schema = scope.compile_and_return(builder::schema(|s| {
114        s.format("email");
115    }).into_json(), true).ok().unwrap();
116
117    assert_eq!(schema.validate(&to_value(&"ad@ress").unwrap()).is_valid(), true);
118    assert_eq!(schema.validate(&to_value(&"add.ress+fd@domain.co.com").unwrap()).is_valid(), true);
119    assert_eq!(schema.validate(&to_value(&"add:re").unwrap()).is_valid(), false);
120}
121
122#[test]
123fn validate_hostname() {
124    let mut scope = scope::Scope::new();
125    let schema = scope.compile_and_return(builder::schema(|s| {
126        s.format("hostname");
127    }).into_json(), true).ok().unwrap();
128
129    assert_eq!(schema.validate(&to_value(&"example").unwrap()).is_valid(), true);
130    assert_eq!(schema.validate(&to_value(&"example.com").unwrap()).is_valid(), true);
131    assert_eq!(schema.validate(&to_value(&"ex:ample").unwrap()).is_valid(), false);
132}
133
134#[test]
135fn validate_ipv4() {
136    let mut scope = scope::Scope::new();
137    let schema = scope.compile_and_return(builder::schema(|s| {
138        s.format("ipv4");
139    }).into_json(), true).ok().unwrap();
140
141    assert_eq!(schema.validate(&to_value(&"127.0.0.1").unwrap()).is_valid(), true);
142    assert_eq!(schema.validate(&to_value(&"8.8.8.8").unwrap()).is_valid(), true);
143    assert_eq!(schema.validate(&to_value(&"::::0.0.0.0").unwrap()).is_valid(), false);
144}
145
146#[test]
147fn validate_ipv6() {
148    let mut scope = scope::Scope::new();
149    let schema = scope.compile_and_return(builder::schema(|s| {
150        s.format("ipv6");
151    }).into_json(), true).ok().unwrap();
152
153    assert_eq!(schema.validate(&to_value(&"FE80:0000:0000:0000:0202:B3FF:FE1E:8329").unwrap()).is_valid(), true);
154    assert_eq!(schema.validate(&to_value(&"127.0.0.1").unwrap()).is_valid(), false);
155}
156
157#[test]
158fn validate_uri() {
159    let mut scope = scope::Scope::new();
160    let schema = scope.compile_and_return(builder::schema(|s| {
161        s.format("uri");
162    }).into_json(), true).ok().unwrap();
163
164    assert_eq!(schema.validate(&to_value(&"http://example.com").unwrap()).is_valid(), true);
165    assert_eq!(schema.validate(&to_value(&"some-wrong").unwrap()).is_valid(), false);
166}
167
168#[test]
169fn validate_uuid() {
170    let mut scope = scope::Scope::new();
171    let schema = scope.compile_and_return(builder::schema(|s| {
172        s.format("uuid");
173    }).into_json(), true).ok().unwrap();
174
175    assert_eq!(schema.validate(&to_value(&"2f5a2593-7481-49e2-9911-8fe2ad069aac").unwrap()).is_valid(), true);
176    assert_eq!(schema.validate(&to_value(&"2f5a2593748149e299118fe2ad069aac").unwrap()).is_valid(), true);
177    assert_eq!(schema.validate(&to_value(&"2f5a2593-7481-49e2-9911-8fe2ad06").unwrap()).is_valid(), false);
178}