valico/json_schema/validators/
items.rs

1use serde_json::{Value};
2use std::cmp;
3use url;
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(&self, val: &Value, path: &str, scope: &scope::Scope) -> super::ValidationState {
28        let array = nonstrict_process!(val.as_array(), path);
29
30        let mut state = super::ValidationState::new();
31
32        match self.items {
33            Some(ItemsKind::Schema(ref url)) => {
34                // Just validate all items against the schema
35
36                let schema = scope.resolve(url);
37                if schema.is_some() {
38                    let schema = schema.unwrap();
39                    for (idx, item) in array.iter().enumerate() {
40                        let item_path = [path, idx.to_string().as_ref()].join("/");
41                        state.append(schema.validate_in(item, item_path.as_ref()));
42                    }
43                } else {
44                    state.missing.push(url.clone());
45                }
46            },
47            Some(ItemsKind::Array(ref urls)) => {
48                let min = cmp::min(urls.len(), array.len());
49
50                // Validate against schemas
51                for idx in 0..min {
52                    let schema = scope.resolve(&urls[idx]);
53                    let item = &array[idx];
54
55                    if schema.is_some() {
56                        let item_path = [path, idx.to_string().as_ref()].join("/");
57                        state.append(schema.unwrap().validate_in(item, item_path.as_ref()))
58                    } else {
59                        state.missing.push(urls[idx].clone())
60                    }
61                }
62
63                // Validate agains additional items
64                if array.len() > urls.len() {
65                    match self.additional {
66                        Some(AdditionalKind::Boolean(allow)) if allow == false => {
67                            state.errors.push(Box::new(
68                                errors::Items {
69                                    path: path.to_string(),
70                                    detail: "Additional items are not allowed".to_string()
71                                }
72                            ))
73                        },
74                        Some(AdditionalKind::Schema(ref url)) => {
75                            let schema = scope.resolve(url);
76                            if schema.is_some() {
77                                let schema = schema.unwrap();
78                                for (idx, item) in array[urls.len()..].iter().enumerate() {
79                                    let item_path = [path, idx.to_string().as_ref()].join("/");
80                                    state.append(schema.validate_in(item, item_path.as_ref()))
81                                }
82                            } else {
83                                state.missing.push(url.clone())
84                            }
85                        },
86                        _ => ()
87                    }
88                }
89            }
90            _ => ()
91        }
92
93        state
94    }
95}