valico/json_schema/validators/
items.rs1use serde_json::Value;
2use std::borrow::Cow;
3use std::cmp;
4
5use super::super::errors;
6use super::super::scope;
7
8#[derive(Debug)]
9pub enum ItemsKind {
10 Schema(url::Url),
11 Array(Vec<url::Url>),
12}
13
14#[derive(Debug)]
15pub enum AdditionalKind {
16 Boolean(bool),
17 Schema(url::Url),
18}
19
20#[allow(missing_copy_implementations)]
21pub struct Items {
22 pub items: Option<ItemsKind>,
23 pub additional: Option<AdditionalKind>,
24}
25
26impl super::Validator for Items {
27 fn validate(
28 &self,
29 val: &Value,
30 path: &str,
31 scope: &scope::Scope,
32 _: &super::ValidationState,
33 ) -> super::ValidationState {
34 let mut array = Cow::Borrowed(nonstrict_process!(val.as_array(), path));
35 let mut state = super::ValidationState::new();
36
37 if scope.supply_defaults {
38 if let Some(ItemsKind::Array(urls)) = self.items.as_ref() {
39 for url in urls.iter().skip(array.len()) {
42 if let Some(schema) = scope.resolve(url) {
43 if let Some(default) = schema.get_default() {
44 array.to_mut().push(default);
45 } else {
46 break;
47 }
48 } else {
49 break;
50 }
51 }
52 }
53 }
54
55 match self.items {
56 Some(ItemsKind::Schema(ref url)) => {
57 let schema = scope.resolve(url);
60 if let Some(schema) = schema {
61 for idx in 0..array.len() {
62 let item = &array[idx];
63 let item_path = [path, idx.to_string().as_ref()].join("/");
64 let mut result = schema.validate_in(item, item_path.as_ref());
65 if result.is_valid() {
66 state.evaluated.insert(item_path);
67 if result.replacement.is_some() {
68 array.to_mut()[idx] = result.replacement.take().unwrap();
69 }
70 }
71 state.append(result);
72 }
73 } else {
74 state.missing.push(url.clone());
75 }
76 }
77 Some(ItemsKind::Array(ref urls)) => {
78 let min = cmp::min(urls.len(), array.len());
79
80 for idx in 0..min {
82 let schema = scope.resolve(&urls[idx]);
83 let item = &array[idx];
84
85 if let Some(schema) = schema {
86 let item_path = [path, idx.to_string().as_ref()].join("/");
87 let mut result = schema.validate_in(item, item_path.as_ref());
88
89 if result.is_valid() {
90 state.evaluated.insert(item_path);
91 if result.replacement.is_some() {
92 array.to_mut()[idx] = result.replacement.take().unwrap();
93 }
94 }
95 state.append(result);
96 } else {
97 state.missing.push(urls[idx].clone())
98 }
99 }
100
101 if array.len() > urls.len() {
103 match self.additional {
104 Some(AdditionalKind::Boolean(allow)) => {
105 if !allow {
106 state.errors.push(Box::new(errors::Items {
107 path: path.to_string(),
108 detail: "Additional items are not allowed".to_string(),
109 }))
110 } else {
111 for idx in urls.len()..array.len() {
112 state
113 .evaluated
114 .insert([path, idx.to_string().as_ref()].join("/"));
115 }
116 }
117 }
118 Some(AdditionalKind::Schema(ref url)) => {
119 let schema = scope.resolve(url);
120 if let Some(schema) = schema {
121 for idx in urls.len()..array.len() {
122 let item = &array[idx];
123 let item_path = [path, idx.to_string().as_ref()].join("/");
124 let mut result = schema.validate_in(item, item_path.as_ref());
125 if result.is_valid() {
126 state.evaluated.insert(item_path);
127 if result.replacement.is_some() {
128 array.to_mut()[idx] =
129 result.replacement.take().unwrap();
130 }
131 }
132 state.append(result);
133 }
134 } else {
135 state.missing.push(url.clone())
136 }
137 }
138 _ => (),
139 }
140 }
141 }
142 _ => (),
143 }
144
145 state.set_replacement(array);
146 state
147 }
148}