valico/json_schema/validators/
properties.rs

1use serde_json::{Value};
2use regex;
3use std::collections;
4use url;
5
6use super::super::errors;
7use super::super::scope;
8
9#[derive(Debug)]
10pub enum AdditionalKind {
11    Boolean(bool),
12    Schema(url::Url)
13}
14
15#[allow(missing_copy_implementations)]
16pub struct Properties {
17    pub properties: collections::HashMap<String, url::Url>,
18    pub additional: AdditionalKind,
19    pub patterns: Vec<(regex::Regex, url::Url)>
20}
21
22impl super::Validator for Properties {
23    fn validate(&self, val: &Value, path: &str, scope: &scope::Scope) -> super::ValidationState {
24        let object = nonstrict_process!(val.as_object(), path);
25        let mut state = super::ValidationState::new();
26
27        'main: for (key, value) in object.iter() {
28            let mut is_property_passed = false;
29            if self.properties.contains_key(key) {
30                let url = self.properties.get(key).unwrap();
31                let schema = scope.resolve(url);
32                if schema.is_some() {
33                    let value_path = [path, key.as_ref()].join("/");
34                    state.append(schema.unwrap().validate_in(value, value_path.as_ref()))
35                } else {
36                    state.missing.push(url.clone())
37                }
38
39               is_property_passed = true;
40            }
41
42            let mut is_pattern_passed = false;
43            for &(ref regex, ref url) in self.patterns.iter() {
44                if regex.is_match(key.as_ref()) {
45                    let schema = scope.resolve(url);
46                    if schema.is_some() {
47                        let value_path = [path, key.as_ref()].join("/");
48                        state.append(schema.unwrap().validate_in(value, value_path.as_ref()));
49                        is_pattern_passed = true;
50                    } else {
51                        state.missing.push(url.clone())
52                    }
53                }
54            }
55
56            if is_property_passed || is_pattern_passed {
57                continue 'main;
58            }
59
60            match self.additional {
61                AdditionalKind::Boolean(allowed) if allowed == false => {
62                    state.errors.push(Box::new(
63                        errors::Properties {
64                            path: path.to_string(),
65                            detail: "Additional properties are not allowed".to_string()
66                        }
67                    ))
68                },
69                AdditionalKind::Schema(ref url) => {
70                    let schema = scope.resolve(url);
71
72                    if schema.is_some() {
73                        let value_path = [path, key.as_ref()].join("/");
74                        state.append(schema.unwrap().validate_in(value, value_path.as_ref()))
75                    } else {
76                        state.missing.push(url.clone())
77                    }
78                },
79                // Additional are allowed here
80                _ => ()
81            }
82        }
83
84        state
85    }
86}