Skip to main content

valico/json_schema/validators/
unevaluated.rs

1use std::{borrow::Cow, collections::HashSet};
2
3use crate::json_schema::errors;
4
5pub enum UnevaluatedSchema {
6    Bool(bool),
7    Schema(url::Url),
8}
9
10pub struct Unevaluated {
11    pub is_items: bool,
12    pub schema: UnevaluatedSchema,
13}
14
15impl Unevaluated {
16    fn check_one_item(
17        &self,
18        item_path: String,
19        item: &serde_json::Value,
20        scope: &crate::json_schema::scope::Scope,
21    ) -> super::ValidationState {
22        let mut state = super::ValidationState::new();
23        match self.schema {
24            UnevaluatedSchema::Bool(allow_unevaluated) => {
25                if !allow_unevaluated {
26                    state.errors.push(Box::new(errors::Unevaluated {
27                        path: item_path,
28                        detail: if self.is_items {
29                            "Unevaluated items are not allowed".to_string()
30                        } else {
31                            "Unevaluated properties are not allowed".to_string()
32                        },
33                    }));
34                } else {
35                    state.evaluated.insert(item_path);
36                }
37            }
38            UnevaluatedSchema::Schema(ref url) => {
39                let schema = scope.resolve(url);
40                if let Some(schema) = schema {
41                    let mut result = schema.validate_in(item, item_path.as_ref());
42                    if result.is_valid() {
43                        state.evaluated.insert(item_path);
44                        state.replacement = result.replacement.take();
45                    }
46
47                    state.append(result);
48                } else {
49                    state.missing.push(url.clone())
50                }
51            }
52        }
53
54        state
55    }
56}
57
58impl super::Validator for Unevaluated {
59    fn validate(
60        &self,
61        val: &serde_json::Value,
62        path: &str,
63        scope: &crate::json_schema::scope::Scope,
64        state: &super::ValidationState,
65    ) -> super::ValidationState {
66        let evaluated_children: HashSet<_> = state
67            .evaluated
68            .iter()
69            .filter(|i| i.starts_with(path))
70            .collect();
71        let mut state = super::ValidationState::new();
72
73        if self.is_items {
74            let mut array = Cow::Borrowed(nonstrict_process!(val.as_array(), path));
75
76            for idx in 0..array.len() {
77                let item_path = [path, idx.to_string().as_ref()].join("/");
78                let item = &array[idx];
79                if evaluated_children.contains(&item_path) {
80                    continue;
81                }
82
83                let mut result = self.check_one_item(item_path, item, scope);
84                if result.replacement.is_some() {
85                    array.to_mut()[idx] = result.replacement.take().unwrap();
86                }
87                state.append(result);
88            }
89
90            state.set_replacement(array);
91        } else {
92            let mut object = nonstrict_process!(val.as_object(), path).clone();
93            let mut changed = false;
94            for (k, item) in object.iter_mut() {
95                let item_path = [path, k].join("/");
96
97                if evaluated_children.contains(&item_path) {
98                    continue;
99                }
100
101                let mut result = self.check_one_item(item_path, item, scope);
102                if result.replacement.is_some() {
103                    *item = result.replacement.take().unwrap();
104                    changed = true;
105                }
106                state.append(result);
107            }
108
109            if changed {
110                state.set_replacement(std::borrow::Cow::Owned::<
111                    serde_json::Map<String, serde_json::Value>,
112                >(object));
113            }
114        }
115
116        state
117    }
118}