fog_pack/validator/
array.rs

1use super::*;
2use crate::error::{Error, Result};
3use crate::{de::FogDeserializer, element::*, value::Value, value_ref::ValueRef};
4use serde::{Deserialize, Serialize};
5use std::{default::Default, iter::repeat};
6
7#[inline]
8fn is_false(v: &bool) -> bool {
9    !v
10}
11#[inline]
12fn validator_is_any(v: &Validator) -> bool {
13    *v == Validator::Any
14}
15
16#[inline]
17fn u32_is_zero(v: &u32) -> bool {
18    *v == 0
19}
20
21#[inline]
22fn u32_is_max(v: &u32) -> bool {
23    *v == u32::MAX
24}
25
26/// Validator for arrays.
27///
28/// This validator type will only pass array values. Validation passes if:
29///
30/// - If the `in` list is not empty, the array must be among the arrays in the list.
31/// - The array must not be among the arrays in the `nin` list.
32/// - The arrays's length is less than or equal to the value in `max_len`.
33/// - The arrays's length is greater than or equal to the value in `min_len`.
34/// - If `unique` is true, the array items are all unique.
35/// - For each validator in the `contains` list, at least one item in the array passes.
36/// - Each item in the array is checked with a validator at the same index in the `prefix` array.
37///     All validators must pass. If there is no validator at the same index, the validator in
38///     `items` must pass. If a validator is not used, it passes automatially.
39///
40/// # Defaults
41///
42/// Fields that aren't specified for the validator use their defaults instead. The defaults for
43/// each field are:
44///
45/// - comment: ""
46/// - contains: empty
47/// - items: Validator::Any
48/// - prefix: empty
49/// - max_len: u32::MAX
50/// - min_len: u32::MIN
51/// - in_list: empty
52/// - nin_list: empty
53/// - unique: false
54/// - query: false
55/// - array: false
56/// - contains_ok: false
57/// - unique_ok: false
58/// - size: false
59///
60/// # Query Checking
61///
62/// Queries for arrays are only allowed to use non-default values for each field if the
63/// corresponding query permission is set in the schema's validator:
64///
65/// - query: `in` and `nin` lists
66/// - array: `prefix` and `items`
67/// - contains_ok: `contains`
68/// - unique_ok: `unique`
69/// - size: `max_len` and `min_len`
70///
71/// In addition, sub-validators in the query are matched against the schema's sub-validators:
72///
73/// - Each validator in `contains` is checked against all of the schema's `prefix` validators, as
74///     well as its `items` validator.
75/// - The `items` validator is checked against the schema's `items' validator
76/// - The `prefix` validators are checked against the schema's `prefix` validators. Unmatched
77///     query validators are checked against the schema's `items` validator.
78///
79#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
80#[serde(deny_unknown_fields, default)]
81pub struct ArrayValidator {
82    /// An optional comment explaining the validator.
83    #[serde(skip_serializing_if = "String::is_empty")]
84    pub comment: String,
85    /// For each validator in this array, at least one item in the array must pass the validator.
86    #[serde(skip_serializing_if = "Vec::is_empty")]
87    pub contains: Vec<Validator>,
88    /// A validator that each item in the array must pass, unless it is instead checked by
89    /// `prefix`.
90    #[serde(skip_serializing_if = "validator_is_any")]
91    pub items: Box<Validator>,
92    /// An array of validators, which are matched up against the items in the array. Unmatched
93    /// validators automatically pass, while unmatched items are checked against the `items`
94    /// Validator.
95    #[serde(skip_serializing_if = "Vec::is_empty")]
96    pub prefix: Vec<Validator>,
97    /// The maximum allowed number of items in the array.
98    #[serde(skip_serializing_if = "u32_is_max")]
99    pub max_len: u32,
100    /// The minimum allowed number of items in the array.
101    #[serde(skip_serializing_if = "u32_is_zero")]
102    pub min_len: u32,
103    /// A vector of specific allowed values, stored under the `in` field. If empty, this vector is not checked against.
104    #[serde(rename = "in", skip_serializing_if = "Vec::is_empty")]
105    pub in_list: Vec<Vec<Value>>,
106    /// A vector of specific unallowed values, stored under the `nin` field.
107    #[serde(rename = "nin", skip_serializing_if = "Vec::is_empty")]
108    pub nin_list: Vec<Vec<Value>>,
109    /// If set, all items in the array must be unique.
110    #[serde(skip_serializing_if = "is_false")]
111    pub unique: bool,
112    /// If true, queries against matching spots may have values in the `in` or `nin` lists.
113    #[serde(skip_serializing_if = "is_false")]
114    pub query: bool,
115    /// If true, queries against matching spots may use `items` and `prefix`.
116    #[serde(skip_serializing_if = "is_false")]
117    pub array: bool,
118    /// If true, queries against matching spots may use `contains`.
119    #[serde(skip_serializing_if = "is_false")]
120    pub contains_ok: bool,
121    /// If true, queries against matching spots may use `unique`.
122    #[serde(skip_serializing_if = "is_false")]
123    pub unique_ok: bool,
124    /// If true, queries against matching spots may use `max_len` and `min_len`.
125    #[serde(skip_serializing_if = "is_false")]
126    pub size: bool,
127}
128
129impl Default for ArrayValidator {
130    fn default() -> Self {
131        Self {
132            comment: String::new(),
133            contains: Vec::new(),
134            items: Box::new(Validator::Any),
135            prefix: Vec::new(),
136            max_len: u32::MAX,
137            min_len: u32::MIN,
138            in_list: Vec::new(),
139            nin_list: Vec::new(),
140            unique: false,
141            query: false,
142            array: false,
143            contains_ok: false,
144            unique_ok: false,
145            size: false,
146        }
147    }
148}
149
150impl ArrayValidator {
151    /// Make a new validator with the default configuration.
152    pub fn new() -> Self {
153        Self::default()
154    }
155
156    /// Set a comment for the validator.
157    pub fn comment(mut self, comment: impl Into<String>) -> Self {
158        self.comment = comment.into();
159        self
160    }
161
162    /// Extend the `contains` list with another validator
163    pub fn contains_add(mut self, validator: Validator) -> Self {
164        self.contains.push(validator);
165        self
166    }
167
168    /// Set the `items` validator.
169    pub fn items(mut self, items: Validator) -> Self {
170        self.items = Box::new(items);
171        self
172    }
173
174    /// Extend the `prefix` list with another validator
175    pub fn prefix_add(mut self, prefix: Validator) -> Self {
176        self.prefix.push(prefix);
177        self
178    }
179
180    /// Set the maximum number of allowed bytes.
181    pub fn max_len(mut self, max_len: u32) -> Self {
182        self.max_len = max_len;
183        self
184    }
185
186    /// Set the minimum number of allowed bytes.
187    pub fn min_len(mut self, min_len: u32) -> Self {
188        self.min_len = min_len;
189        self
190    }
191
192    /// Add a value to the `in` list.
193    pub fn in_add(mut self, add: impl Into<Vec<Value>>) -> Self {
194        self.in_list.push(add.into());
195        self
196    }
197
198    /// Add a value to the `nin` list.
199    pub fn nin_add(mut self, add: impl Into<Vec<Value>>) -> Self {
200        self.nin_list.push(add.into());
201        self
202    }
203
204    /// Set whether the items in the array must be unique.
205    pub fn unique(mut self, unique: bool) -> Self {
206        self.unique = unique;
207        self
208    }
209
210    /// Set whether or not queries can use the `in` and `nin` lists.
211    pub fn query(mut self, query: bool) -> Self {
212        self.query = query;
213        self
214    }
215
216    /// Set whether or not queries can use the `items` and `prefix` values.
217    pub fn array(mut self, array: bool) -> Self {
218        self.array = array;
219        self
220    }
221
222    /// Set whether or not queries can use the `contains` value.
223    pub fn contains_ok(mut self, contains_ok: bool) -> Self {
224        self.contains_ok = contains_ok;
225        self
226    }
227
228    /// Set whether or not queries can use the `unique` setting.
229    pub fn unique_ok(mut self, unique_ok: bool) -> Self {
230        self.unique_ok = unique_ok;
231        self
232    }
233
234    /// Set whether or not queries can use the `max_len` and `min_len` values.
235    pub fn size(mut self, size: bool) -> Self {
236        self.size = size;
237        self
238    }
239
240    /// Build this into a [`Validator`] enum.
241    pub fn build(self) -> Validator {
242        Validator::Array(Box::new(self))
243    }
244
245    pub(crate) fn validate<'de, 'c>(
246        &'c self,
247        types: &'c BTreeMap<String, Validator>,
248        mut parser: Parser<'de>,
249        mut checklist: Option<Checklist<'c>>,
250    ) -> Result<(Parser<'de>, Option<Checklist<'c>>)> {
251        let val_parser = parser.clone();
252        let elem = parser
253            .next()
254            .ok_or_else(|| Error::FailValidate("Expected an array".to_string()))??;
255        let len = if let Element::Array(len) = elem {
256            len
257        } else {
258            return Err(Error::FailValidate(format!(
259                "Expected Array, got {}",
260                elem.name()
261            )));
262        };
263
264        if (len as u32) > self.max_len {
265            return Err(Error::FailValidate(format!(
266                "Array is {} elements, longer than maximum allowed of {}",
267                len, self.max_len
268            )));
269        }
270        if (len as u32) < self.min_len {
271            return Err(Error::FailValidate(format!(
272                "Array is {} elements, shorter than minimum allowed of {}",
273                len, self.min_len
274            )));
275        }
276
277        // Check all the requirements that require parsing the entire array
278        if self.unique || !self.in_list.is_empty() || !self.nin_list.is_empty() {
279            let mut de = FogDeserializer::from_parser(val_parser);
280            let array = Vec::<ValueRef>::deserialize(&mut de)?;
281
282            if !self.in_list.is_empty() && !self.in_list.iter().any(|v| *v == array) {
283                return Err(Error::FailValidate("Array is not on `in` list".to_string()));
284            }
285
286            if self.nin_list.iter().any(|v| *v == array) {
287                return Err(Error::FailValidate("Array is on `nin` list".to_string()));
288            }
289
290            if self.unique
291                && array
292                    .iter()
293                    .enumerate()
294                    .any(|(index, lhs)| array.iter().skip(index).any(|rhs| lhs == rhs))
295            {
296                return Err(Error::FailValidate(
297                    "Array does not contain unique elements".to_string(),
298                ));
299            }
300        }
301
302        // Loop through each item, verifying it with the appropriate validator
303        let mut contains_result = vec![false; self.contains.len()];
304        let mut validators = self.prefix.iter().chain(repeat(self.items.as_ref()));
305        for _ in 0..len {
306            // If we have a "contains", check
307            if !self.contains.is_empty() {
308                self.contains
309                    .iter()
310                    .zip(contains_result.iter_mut())
311                    .for_each(|(validator, passed)| {
312                        if !*passed {
313                            let result =
314                                validator.validate(types, parser.clone(), checklist.clone());
315                            if let Ok((_, c)) = result {
316                                *passed = true;
317                                checklist = c;
318                            }
319                        }
320                    });
321            }
322            let (p, c) = validators
323                .next()
324                .unwrap()
325                .validate(types, parser, checklist)?;
326            parser = p;
327            checklist = c;
328        }
329
330        if !contains_result.iter().all(|x| *x) {
331            let mut err_str = String::from("Array was missing items satisfying `contains` list:");
332            let iter = contains_result
333                .iter()
334                .enumerate()
335                .filter(|(_, pass)| !**pass)
336                .map(|(index, _)| format!(" {},", index));
337            err_str.extend(iter);
338            err_str.pop(); // Remove the final comma
339            return Err(Error::FailValidate(err_str));
340        }
341        Ok((parser, checklist))
342    }
343
344    fn query_check_self(
345        &self,
346        types: &BTreeMap<String, Validator>,
347        other: &ArrayValidator,
348    ) -> bool {
349        let initial_check = (self.query || (other.in_list.is_empty() && other.nin_list.is_empty()))
350            && (self.array || (other.prefix.is_empty() && validator_is_any(&other.items)))
351            && (self.contains_ok || other.contains.is_empty())
352            && (self.unique_ok || !other.unique)
353            && (self.size || (u32_is_max(&other.max_len) && u32_is_zero(&other.min_len)));
354        if !initial_check {
355            return false;
356        }
357        if self.contains_ok {
358            let contains_ok = other.contains.iter().all(|other| {
359                self.items.query_check(types, other)
360                    && self
361                        .prefix
362                        .iter()
363                        .all(|mine| mine.query_check(types, other))
364            });
365            if !contains_ok {
366                return false;
367            }
368        }
369        if self.array {
370            // Make sure item_type is OK, then check all items against their matching validator
371            self.items.query_check(types, other.items.as_ref())
372                && self
373                    .prefix
374                    .iter()
375                    .chain(repeat(self.items.as_ref()))
376                    .zip(other.prefix.iter().chain(repeat(other.items.as_ref())))
377                    .take(self.prefix.len().max(other.prefix.len()))
378                    .all(|(mine, other)| mine.query_check(types, other))
379        } else {
380            true
381        }
382    }
383
384    pub(crate) fn query_check(
385        &self,
386        types: &BTreeMap<String, Validator>,
387        other: &Validator,
388    ) -> bool {
389        match other {
390            Validator::Array(other) => self.query_check_self(types, other),
391            Validator::Multi(list) => list.iter().all(|other| match other {
392                Validator::Array(other) => self.query_check_self(types, other),
393                _ => false,
394            }),
395            Validator::Any => true,
396            _ => false,
397        }
398    }
399}
400
401#[cfg(test)]
402mod test {
403    use super::*;
404    use crate::{de::FogDeserializer, ser::FogSerializer};
405
406    #[test]
407    fn ser_default() {
408        // Should be an empty map if we use the defaults
409        let schema = ArrayValidator::default();
410        let mut ser = FogSerializer::default();
411        schema.serialize(&mut ser).unwrap();
412        let expected: Vec<u8> = vec![0x80];
413        let actual = ser.finish();
414        println!("expected: {:x?}", expected);
415        println!("actual:   {:x?}", actual);
416        assert_eq!(expected, actual);
417
418        let mut de = FogDeserializer::with_debug(&actual, "    ");
419        let decoded = ArrayValidator::deserialize(&mut de).unwrap();
420        println!("{}", de.get_debug().unwrap());
421        assert_eq!(schema, decoded);
422    }
423}