Skip to main content

valico/json_schema/validators/
of.rs

1use serde_json::Value;
2use std::borrow::Cow;
3use std::collections::HashSet;
4
5use super::super::errors;
6use super::super::scope;
7
8#[allow(missing_copy_implementations)]
9pub struct AllOf {
10    pub schemes: Vec<url::Url>,
11}
12
13impl super::Validator for AllOf {
14    fn validate(
15        &self,
16        val: &Value,
17        path: &str,
18        scope: &scope::Scope,
19        _: &super::ValidationState,
20    ) -> super::ValidationState {
21        let mut state = super::ValidationState::new();
22        let mut val = Cow::Borrowed(val);
23
24        // first get all relevant schemas
25        let schemas = self
26            .schemes
27            .iter()
28            .map(|url| (url, scope.resolve(url)))
29            .filter_map(|(url, opt)| {
30                if opt.is_none() {
31                    state.missing.push(url.clone())
32                }
33                opt
34            })
35            .collect::<Vec<_>>();
36
37        // first pass to populate all defaults (if enabled)
38        for schema in schemas.iter() {
39            let mut result = schema.validate_in(&val, path);
40            if result.is_valid() && result.replacement.is_some() {
41                *val.to_mut() = result.replacement.take().unwrap();
42            }
43            state.append(result);
44        }
45        if !state.is_valid() {
46            return state;
47        }
48
49        // second pass if defaults are enabled to check that the result is stable
50        if let Cow::Owned(v) = val {
51            let mut second = Cow::Borrowed(&v);
52
53            for schema in schemas.iter() {
54                let mut result = schema.validate_in(&second, path);
55                if result.is_valid() && result.replacement.is_some() {
56                    *second.to_mut() = result.replacement.take().unwrap();
57                }
58                state.append(result);
59            }
60            if let Cow::Owned(_) = second {
61                state.errors.push(Box::new(errors::DivergentDefaults {
62                    path: path.to_string(),
63                }));
64            }
65            if !state.is_valid() {
66                return state;
67            }
68            val = Cow::Owned(v);
69        }
70
71        state.set_replacement(val);
72        state
73    }
74}
75
76#[allow(missing_copy_implementations)]
77pub struct AnyOf {
78    pub schemes: Vec<url::Url>,
79}
80
81impl super::Validator for AnyOf {
82    fn validate(
83        &self,
84        val: &Value,
85        path: &str,
86        scope: &scope::Scope,
87        _: &super::ValidationState,
88    ) -> super::ValidationState {
89        let mut state = super::ValidationState::new();
90        let mut val = Cow::Borrowed(val);
91
92        let mut invalid_states = vec![];
93        // The "best" state is defined as "the one that validates the most items".
94        let mut evaluated: HashSet<String> = HashSet::new();
95        let mut valid = false;
96        for url in self.schemes.iter() {
97            let schema = scope.resolve(url);
98
99            if let Some(schema) = schema {
100                let mut result = schema.validate_in(&val, path);
101
102                state.missing.extend(result.missing.clone());
103
104                if result.is_valid() {
105                    if let Some(result) = result.replacement.take() {
106                        *val.to_mut() = result;
107                    }
108                    valid = true;
109                    evaluated.extend(result.evaluated);
110                    // Cannot short-circuit here as "unevaluatedItems" requires that we find the "best" state.
111                } else {
112                    invalid_states.push(result)
113                }
114            } else {
115                state.missing.push(url.clone())
116            }
117        }
118
119        if !valid {
120            state.errors.push(Box::new(errors::AnyOf {
121                path: path.to_string(),
122                states: invalid_states,
123            }))
124        } else {
125            state.evaluated.extend(evaluated);
126        }
127
128        state.set_replacement(val);
129        state
130    }
131}
132
133#[allow(missing_copy_implementations)]
134pub struct OneOf {
135    pub schemes: Vec<url::Url>,
136}
137
138impl super::Validator for OneOf {
139    fn validate(
140        &self,
141        val: &Value,
142        path: &str,
143        scope: &scope::Scope,
144        _: &super::ValidationState,
145    ) -> super::ValidationState {
146        let mut state = super::ValidationState::new();
147        let mut val = Cow::Borrowed(val);
148
149        let mut states = vec![];
150        let mut valid = 0;
151        let mut evaluated = HashSet::new();
152        for url in self.schemes.iter() {
153            let schema = scope.resolve(url);
154
155            if let Some(schema) = schema {
156                let mut result = schema.validate_in(&val, path);
157
158                state.missing.extend(result.missing.clone());
159
160                if result.is_valid() {
161                    if let Some(result) = result.replacement.take() {
162                        *val.to_mut() = result;
163                    }
164                    valid += 1;
165                    evaluated = result.evaluated;
166                } else {
167                    states.push(result)
168                }
169            } else {
170                state.missing.push(url.clone())
171            }
172        }
173
174        if valid != 1 {
175            state.errors.push(Box::new(errors::OneOf {
176                path: path.to_string(),
177                states,
178            }))
179        } else {
180            state.evaluated = evaluated;
181        }
182
183        state.set_replacement(val);
184        state
185    }
186}