valico/json_schema/validators/
unevaluated.rs1use 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}