Skip to main content

valico/json_schema/validators/
contains.rs

1use serde_json::Value;
2use std::borrow::Cow;
3
4use super::super::errors;
5use super::super::scope;
6
7#[allow(missing_copy_implementations)]
8pub struct Contains {
9    pub url: url::Url,
10    pub max_contains: Option<u64>,
11    pub min_contains: Option<u64>,
12}
13
14impl super::Validator for Contains {
15    fn validate(
16        &self,
17        val: &Value,
18        path: &str,
19        scope: &scope::Scope,
20        _: &super::ValidationState,
21    ) -> super::ValidationState {
22        let mut array = Cow::Borrowed(nonstrict_process!(val.as_array(), path));
23
24        let schema = scope.resolve(&self.url);
25        let mut state = super::ValidationState::new();
26
27        if let Some(schema) = schema {
28            let mut matched_count = 0;
29            for idx in 0..array.len() {
30                let item_path = [path, idx.to_string().as_ref()].join("/");
31                let item = &array[idx];
32                let mut result = schema.validate_in(item, item_path.as_ref());
33                if result.is_valid() {
34                    matched_count += 1;
35                    if let Some(result) = result.replacement.take() {
36                        array.to_mut()[idx] = result;
37                    }
38                    if self.max_contains.is_none() && self.min_contains.is_none() {
39                        break;
40                    }
41                }
42            }
43
44            if matched_count == 0 && self.min_contains != Some(0) {
45                state.errors.push(Box::new(errors::Contains {
46                    path: path.to_string(),
47                }))
48            }
49
50            if self
51                .max_contains
52                .map(|max| matched_count > max)
53                .unwrap_or(false)
54            {
55                state.errors.push(Box::new(errors::ContainsMinMax {
56                    path: path.to_string(),
57                }));
58            }
59
60            if self
61                .min_contains
62                .map(|min| matched_count < min)
63                .unwrap_or(false)
64            {
65                state.errors.push(Box::new(errors::ContainsMinMax {
66                    path: path.to_string(),
67                }));
68            }
69        } else {
70            state.missing.push(self.url.clone());
71        }
72
73        state.set_replacement(array);
74        state
75    }
76}