valico/json_schema/keywords/
items.rs
1use serde_json::{Value};
2
3use super::super::schema;
4use super::super::validators;
5use super::super::helpers;
6
7#[allow(missing_copy_implementations)]
8pub struct Items;
9impl super::Keyword for Items {
10 fn compile(&self, def: &Value, ctx: &schema::WalkContext) -> super::KeywordResult {
11 let maybe_items = def.get("items");
12 let maybe_additional = def.get("additionalItems");
13
14 if !(maybe_items.is_some() || maybe_additional.is_some()) {
15 return Ok(None)
16 }
17
18 let items = if maybe_items.is_some() {
19 let items_val = maybe_items.unwrap();
20 Some(if items_val.is_object() {
21
22 validators::items::ItemsKind::Schema(
23 helpers::alter_fragment_path(ctx.url.clone(), [
24 ctx.escaped_fragment().as_ref(),
25 "items"
26 ].join("/"))
27 )
28
29 } else if items_val.is_array() {
30
31 let mut schemas = vec![];
32 for (idx, item) in items_val.as_array().unwrap().iter().enumerate() {
33 if item.is_object() {
34 schemas.push(
35 helpers::alter_fragment_path(ctx.url.clone(), [
36 ctx.escaped_fragment().as_ref(),
37 "items",
38 idx.to_string().as_ref()
39 ].join("/"))
40 )
41 } else {
42 return Err(schema::SchemaError::Malformed {
43 path: ctx.fragment.join("/"),
44 detail: "Items of this array MUST be objects".to_string()
45 })
46 }
47 }
48
49 validators::items::ItemsKind::Array(schemas)
50
51 } else {
52
53 return Err(schema::SchemaError::Malformed {
54 path: ctx.fragment.join("/"),
55 detail: "`items` must be an object or an array".to_string()
56 })
57
58 })
59 } else {
60 None
61 };
62
63 let additional_items = if maybe_additional.is_some() {
64 let additional_val = maybe_additional.unwrap();
65 Some(if additional_val.is_boolean() {
66
67 validators::items::AdditionalKind::Boolean(additional_val.as_bool().unwrap())
68
69 } else if additional_val.is_object() {
70
71 validators::items::AdditionalKind::Schema(
72 helpers::alter_fragment_path(ctx.url.clone(), [
73 ctx.escaped_fragment().as_ref(),
74 "additionalItems"
75 ].join("/"))
76 )
77
78 } else {
79
80 return Err(schema::SchemaError::Malformed {
81 path: ctx.fragment.join("/"),
82 detail: "`additionalItems` must be a boolean or an object".to_string()
83 })
84
85 })
86 } else {
87 None
88 };
89
90 Ok(Some(Box::new(validators::Items {
91 items: items,
92 additional: additional_items
93 })))
94
95 }
96}
97
98#[cfg(test)] use super::super::scope;
99#[cfg(test)] use super::super::builder;
100#[cfg(test)] use serde_json::to_value;
101
102#[test]
103fn validate_items_with_schema() {
104 let mut scope = scope::Scope::new();
105 let schema = scope.compile_and_return(builder::schema(|s| {
106 s.items_schema(|items| {
107 items.minimum(5f64, false);
108 items.maximum(10f64, false);
109 });
110 }).into_json(), true).ok().unwrap();
111
112 assert_eq!(schema.validate(&to_value(&[5,6,7,8,9,10]).unwrap()).is_valid(), true);
113 assert_eq!(schema.validate(&to_value(&[4,5,6,7,8,9,10]).unwrap()).is_valid(), false);
114 assert_eq!(schema.validate(&to_value(&[5,6,7,8,9,10,11]).unwrap()).is_valid(), false);
115}
116
117#[test]
118fn validate_items_with_array_of_schemes() {
119 let mut scope = scope::Scope::new();
120 let schema = scope.compile_and_return(builder::schema(|s| {
121 s.items_array(|items| {
122 items.push(|item| {
123 item.minimum(1f64, false);
124 item.maximum(3f64, false);
125 });
126 items.push(|item| {
127 item.minimum(3f64, false);
128 item.maximum(6f64, false);
129 });
130 })
131 }).into_json(), true).ok().unwrap();
132
133 assert_eq!(schema.validate(&to_value(&[1]).unwrap()).is_valid(), true);
134 assert_eq!(schema.validate(&to_value(&[1,3]).unwrap()).is_valid(), true);
135 assert_eq!(schema.validate(&to_value(&[1,3,100]).unwrap()).is_valid(), true);
136 assert_eq!(schema.validate(&to_value(&[4,3]).unwrap()).is_valid(), false);
137 assert_eq!(schema.validate(&to_value(&[1,7]).unwrap()).is_valid(), false);
138 assert_eq!(schema.validate(&to_value(&[4,7]).unwrap()).is_valid(), false);
139}
140
141#[test]
142fn validate_items_with_array_of_schemes_with_additional_bool() {
143 let mut scope = scope::Scope::new();
144 let schema = scope.compile_and_return(builder::schema(|s| {
145 s.items_array(|items| {
146 items.push(|item| {
147 item.minimum(1f64, false);
148 item.maximum(3f64, false);
149 });
150 items.push(|item| {
151 item.minimum(3f64, false);
152 item.maximum(6f64, false);
153 });
154 });
155 s.additional_items(false);
156 }).into_json(), true).ok().unwrap();
157
158 assert_eq!(schema.validate(&to_value(&[1,3,100]).unwrap()).is_valid(), false);
159}
160
161#[test]
162fn validate_items_with_array_of_schemes_with_additional_schema() {
163 let mut scope = scope::Scope::new();
164 let schema = scope.compile_and_return(builder::schema(|s| {
165 s.items_array(|items| {
166 items.push(|item| {
167 item.minimum(1f64, false);
168 item.maximum(3f64, false);
169 });
170 items.push(|item| {
171 item.minimum(3f64, false);
172 item.maximum(6f64, false);
173 });
174 });
175 s.additional_items_schema(|add| {
176 add.maximum(100f64, false)
177 });
178 }).into_json(), true).ok().unwrap();
179
180 assert_eq!(schema.validate(&to_value(&[1,3,100]).unwrap()).is_valid(), true);
181 assert_eq!(schema.validate(&to_value(&[1,3,101]).unwrap()).is_valid(), false);
182}