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}