json_model/attribute/
array_items.rs

1use super::{AllowedType, Attribute};
2use crate::definition::{Definition, Type};
3use crate::error::{Error, ValidationError};
4use crate::validator::{Context, DocumentPath, State};
5
6use std::collections::HashSet;
7
8use serde_json;
9
10#[derive(Debug)]
11pub struct ArrayItems {
12    name: String,
13    ptrs: HashSet<String>,
14}
15
16impl ArrayItems {
17    pub fn new(state: &mut State, mut path: DocumentPath, ctx: &Context) -> Result<Self, Error> {
18        let obj = ctx.raw_definition();
19
20        match Type::new(obj, path.clone())? {
21            Type::Array => (),
22            typ => return Err(Error::ForbiddenType { path, typ }),
23        };
24
25        let defs = match obj.get(ctx.name().as_str()) {
26            Some(items) => match items.as_array() {
27                Some(items_arr) => items_arr,
28                None => {
29                    path.add(ctx.name().as_str());
30                    return Err(Error::InvalidValue {
31                        path,
32                        value: items.clone(),
33                    });
34                }
35            },
36            None => {
37                return Err(Error::MissingAttribute {
38                    path,
39                    attr: ctx.name(),
40                })
41            }
42        };
43
44        path.add(ctx.name().as_str());
45
46        let mut ptrs = HashSet::<String>::new();
47        for (i, d) in defs.iter().enumerate() {
48            let mut path = path.clone();
49            path.add(i.to_string().as_str());
50            let def = Definition::new(state, d, ctx.ptr(), path.clone(), ctx.path())?;
51            ptrs.insert(def.ptr());
52            state.add_definition(def, path)?;
53        }
54
55        Ok(ArrayItems {
56            name: ctx.name(),
57            ptrs,
58        })
59    }
60
61    pub fn allowed_types() -> HashSet<AllowedType> {
62        let mut set = HashSet::<AllowedType>::new();
63        set.insert(AllowedType::new(Type::Array, true));
64        set
65    }
66
67    pub fn build(
68        state: &mut State,
69        path: DocumentPath,
70        ctx: &Context,
71    ) -> Result<Box<Attribute>, Error> {
72        Ok(Box::new(ArrayItems::new(state, path, ctx)?))
73    }
74}
75
76impl Attribute for ArrayItems {
77    fn validate(
78        &self,
79        state: &State,
80        path: Vec<String>,
81        input: &serde_json::Value,
82    ) -> Result<(), ValidationError> {
83        let arr = match input.as_array() {
84            Some(arr) => arr,
85            None => {
86                return Err(ValidationError::Failure {
87                    rule: "type".to_string(),
88                    path: path,
89                    message: "Value must be an array.".to_string(),
90                })
91            }
92        };
93
94        for (i, value) in arr.iter().enumerate() {
95            let mut found = false;
96            let mut path = path.clone();
97            path.push(i.to_string());
98            for ptr in &self.ptrs {
99                let def = match state.get_definition(ptr.as_str()) {
100                    Some(def) => def,
101                    None => return Err(ValidationError::UndefinedDefinition),
102                };
103                if let Ok(_) = def.validate(state, value, path.clone()) {
104                    found = true;
105                    break;
106                }
107            }
108            if !found {
109                return Err(ValidationError::Failure {
110                    rule: "items".to_string(),
111                    path: path,
112                    message: "Value must be a valid array item.".to_string(),
113                });
114            }
115        }
116
117        Ok(())
118    }
119}