fog_pack/validator/
map.rs

1use super::*;
2use crate::error::{Error, Result};
3use crate::{de::FogDeserializer, element::*, value::Value, value_ref::ValueRef};
4use serde::{Deserialize, Deserializer, Serialize};
5use std::default::Default;
6
7#[inline]
8fn is_false(v: &bool) -> bool {
9    !v
10}
11
12#[inline]
13fn u32_is_zero(v: &u32) -> bool {
14    *v == 0
15}
16
17#[inline]
18fn u32_is_max(v: &u32) -> bool {
19    *v == u32::MAX
20}
21
22fn get_str_validator<'de, D: Deserializer<'de>>(
23    deserializer: D,
24) -> Result<Option<Box<StrValidator>>, D::Error> {
25    // Decode the validator. If this function is called, there should be an actual validator
26    // present. Otherwise we fail. In other words, no `null` allowed.
27    Ok(Some(Box::new(StrValidator::deserialize(deserializer)?)))
28}
29
30fn get_validator<'de, D: Deserializer<'de>>(
31    deserializer: D,
32) -> Result<Option<Box<Validator>>, D::Error> {
33    // Decode the validator. If this function is called, there should be an actual validator
34    // present. Otherwise we fail. In other words, no `null` allowed.
35    Ok(Some(Box::new(Validator::deserialize(deserializer)?)))
36}
37
38/// Validator for maps.
39///
40/// This validator will only pass maps, whose keys are strings and values are any valid fog-pack
41/// value. Validation passes if:
42///
43/// - If the `in` list is not empty, the map must be among the maps in the list.
44/// - The map must not be among the maps in the `nin` list.
45/// - The number of key-value pairs in the map is less than or equal to the value in `max_len`.
46/// - The number of key-value pairs in the map is greater than or equal to the value in `min_len`.
47/// - There must be a matching key-value in the map for each key-validator pair in `req` .
48/// - For each key-value pair in the map:
49///     1. If the key is in `req`, the corresponding validator is used to validate the value.
50///     2. If the key is not in `req` but is in `opt`, the corresponding validator is used to
51///        validate the value.
52///     3. if the key is not in `req` or `opt`, the validator for `values` is used to validate the
53///        value, and the validator for `keys` (if present) is used to validate the key.
54///         1. If no validator is present for `keys`, the key passes.
55///         2. If there is no validator for `values`, validation does not pass.
56/// - If `same_len` is not empty, the keys it lists must either all not exist, or if any of them
57///     exist, they must all exist and their values must all be arrays with the same lengths.
58///
59/// Note how each key-value pair must be validated, so an unlimited collection of key-value pairs
60/// isn't allowed unless there is a validator present in `values`.
61///
62/// # Defaults
63///
64/// Fields that aren't specified for the validator use their defaults instead. The defaults for
65/// each field are:
66///
67/// - comment: ""
68/// - max_len: u32::MAX
69/// - min_len: u32::MIN
70/// - keys: None
71/// - values: None
72/// - req: empty
73/// - opt: empty
74/// - same_len: empty
75/// - in_list: empty
76/// - nin_list: empty
77/// - query: false
78/// - size: false
79/// - map_ok: false
80/// - same_len_ok: false
81///
82/// # Query Checking
83///
84/// Queries for maps are only allowed to use non-default values for each field if the
85/// corresponding query permission is set in the schema's validator:
86///
87/// - query: `in` and `nin` lists
88/// - size: `max_len` and `min_len`
89/// - map_ok: `req`, `opt`, `keys`, and `values`
90/// - same_len_ok: `same_len`
91///
92/// In addition, sub-validators in the query are matched against the schema's sub-validators:
93///
94/// - The `values` validator is checked against the schema's `values` validator. If no schema
95///     validator is present, the query is invalid.
96/// - The `keys` string validator is checked against the schema's `keys` string validator. If no
97///     schema validator is present, the query is invalid.
98/// - The `req` validators are checked against the schema's `req`/`opt`/`values` validators,
99///     choosing whichever validator is found first. If no validator is found, the check fails.
100/// - The `opt` validators are checked against the schema's `req`/`opt`/`values` validators,
101///     choosing whichever validator is found first. If no validator is found, the check fails.
102///
103#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
104#[serde(deny_unknown_fields, default)]
105pub struct MapValidator {
106    /// An optional comment explaining the validator.
107    #[serde(skip_serializing_if = "String::is_empty")]
108    pub comment: String,
109    /// The maximum allowed number of key-value pairs in the map.
110    #[serde(skip_serializing_if = "u32_is_max")]
111    pub max_len: u32,
112    /// The minimum allowed number of key-value pairs in the map.
113    #[serde(skip_serializing_if = "u32_is_zero")]
114    pub min_len: u32,
115    /// The optional sub-validator for unknown keys in the map.
116    #[serde(
117        skip_serializing_if = "Option::is_none",
118        deserialize_with = "get_str_validator"
119    )]
120    pub keys: Option<Box<StrValidator>>,
121    /// An optional validator that each value in the map must pass, unless it is instead checked by
122    /// a validator in `req` or `opt`. Unchecked values cause the map to fail validation.
123    #[serde(
124        skip_serializing_if = "Option::is_none",
125        deserialize_with = "get_validator"
126    )]
127    pub values: Option<Box<Validator>>,
128    /// A map whose keys must all be present in a passing map, and whose validators are used to
129    /// check the value held by a matching key in the map.
130    #[serde(skip_serializing_if = "BTreeMap::is_empty")]
131    pub req: BTreeMap<String, Validator>,
132    /// A map whose keys may be present in a map, and whose validators are used to
133    /// check the value held by a matching key in the map, unless it is first checked by a
134    /// validator in `req`.
135    #[serde(skip_serializing_if = "BTreeMap::is_empty")]
136    pub opt: BTreeMap<String, Validator>,
137    /// A vector of specific allowed values, stored under the `in` field. If empty, this vector is not checked against.
138    #[serde(rename = "in", skip_serializing_if = "Vec::is_empty")]
139    pub in_list: Vec<BTreeMap<String, Value>>,
140    /// A vector of specific unallowed values, stored under the `nin` field.
141    #[serde(rename = "nin", skip_serializing_if = "Vec::is_empty")]
142    pub nin_list: Vec<BTreeMap<String, Value>>,
143    /// A vector of which keys must either not exist, or must all exist and contain arrays of the
144    /// same lengths.
145    #[serde(skip_serializing_if = "Vec::is_empty")]
146    pub same_len: Vec<String>,
147    /// If true, queries against matching spots may have values in the `in` or `nin` lists.
148    #[serde(skip_serializing_if = "is_false")]
149    pub query: bool,
150    /// If true, queries against matching spots may use `max_len` and `min_len`.
151    #[serde(skip_serializing_if = "is_false")]
152    pub size: bool,
153    /// If true, queries against matching spots may use `req`, `opt`, `keys`, and `values`.
154    #[serde(skip_serializing_if = "is_false")]
155    pub map_ok: bool,
156    /// If true, queries against matching spots may use `same_len`.
157    #[serde(skip_serializing_if = "is_false")]
158    pub same_len_ok: bool,
159}
160
161impl Default for MapValidator {
162    fn default() -> Self {
163        Self {
164            comment: String::new(),
165            max_len: u32::MAX,
166            min_len: u32::MIN,
167            keys: None,
168            values: None,
169            req: BTreeMap::new(),
170            opt: BTreeMap::new(),
171            in_list: Vec::new(),
172            nin_list: Vec::new(),
173            same_len: Vec::new(),
174            query: false,
175            size: false,
176            map_ok: false,
177            same_len_ok: false,
178        }
179    }
180}
181
182impl MapValidator {
183    /// Make a new validator with the default configuration.
184    pub fn new() -> Self {
185        Self::default()
186    }
187
188    /// Set a comment for the validator.
189    pub fn comment(mut self, comment: impl Into<String>) -> Self {
190        self.comment = comment.into();
191        self
192    }
193
194    /// Set the `values` validator.
195    pub fn values(mut self, values: Validator) -> Self {
196        self.values = Some(Box::new(values));
197        self
198    }
199
200    /// Add a new validator to the `req` map.
201    pub fn req_add(mut self, key: impl Into<String>, req: Validator) -> Self {
202        self.req.insert(key.into(), req);
203        self
204    }
205
206    /// Add a new validator to the `opt` map.
207    pub fn opt_add(mut self, key: impl Into<String>, opt: Validator) -> Self {
208        self.opt.insert(key.into(), opt);
209        self
210    }
211
212    /// Set the Key Validator.
213    pub fn keys(mut self, keys: StrValidator) -> Self {
214        self.keys = Some(Box::new(keys));
215        self
216    }
217
218    /// Set the maximum number of allowed bytes.
219    pub fn max_len(mut self, max_len: u32) -> Self {
220        self.max_len = max_len;
221        self
222    }
223
224    /// Set the minimum number of allowed bytes.
225    pub fn min_len(mut self, min_len: u32) -> Self {
226        self.min_len = min_len;
227        self
228    }
229
230    /// Add a value to the `in` list.
231    pub fn in_add(mut self, add: impl Into<BTreeMap<String, Value>>) -> Self {
232        self.in_list.push(add.into());
233        self
234    }
235
236    /// Add a value to the `nin` list.
237    pub fn nin_add(mut self, add: impl Into<BTreeMap<String, Value>>) -> Self {
238        self.nin_list.push(add.into());
239        self
240    }
241
242    /// Add a key to the `same_len` list.
243    pub fn same_len_add(mut self, add: impl Into<String>) -> Self {
244        self.same_len.push(add.into());
245        self
246    }
247
248    /// Set whether or not queries can use the `in` and `nin` lists.
249    pub fn query(mut self, query: bool) -> Self {
250        self.query = query;
251        self
252    }
253
254    /// Set whether or not queries can use the `max_len` and `min_len` values.
255    pub fn size(mut self, size: bool) -> Self {
256        self.size = size;
257        self
258    }
259
260    /// Set whether or not queries can use the `req`, `opt`, `ban`, and `values` values.
261    pub fn map_ok(mut self, map_ok: bool) -> Self {
262        self.map_ok = map_ok;
263        self
264    }
265
266    /// Set whether or not queries can use the `same_len` value.
267    pub fn same_len_ok(mut self, same_len_ok: bool) -> Self {
268        self.same_len_ok = same_len_ok;
269        self
270    }
271
272    /// Build this into a [`Validator`] enum.
273    pub fn build(self) -> Validator {
274        Validator::Map(Box::new(self))
275    }
276
277    pub(crate) fn validate<'de, 'c>(
278        &'c self,
279        types: &'c BTreeMap<String, Validator>,
280        mut parser: Parser<'de>,
281        mut checklist: Option<Checklist<'c>>,
282    ) -> Result<(Parser<'de>, Option<Checklist<'c>>)> {
283        let val_parser = parser.clone();
284        let elem = parser
285            .next()
286            .ok_or_else(|| Error::FailValidate("Expected a map".to_string()))??;
287        let len = if let Element::Map(len) = elem {
288            len
289        } else {
290            return Err(Error::FailValidate(format!(
291                "Expected Map, got {}",
292                elem.name()
293            )));
294        };
295
296        if (len as u32) > self.max_len {
297            return Err(Error::FailValidate(format!(
298                "Map is {} pairs, longer than maximum allowed of {}",
299                len, self.max_len
300            )));
301        }
302        if (len as u32) < self.min_len {
303            return Err(Error::FailValidate(format!(
304                "Map is {} pairs, shorter than minimum allowed of {}",
305                len, self.min_len
306            )));
307        }
308
309        // Check the requirements that require parsing the entire array
310        if !self.in_list.is_empty() || !self.nin_list.is_empty() {
311            let mut de = FogDeserializer::from_parser(val_parser);
312            let map = BTreeMap::<&str, ValueRef>::deserialize(&mut de)?;
313
314            if !self.in_list.is_empty() {
315                let in_pass = self.in_list.iter().any(|v| {
316                    v.len() == map.len()
317                        && v.iter()
318                            .zip(map.iter())
319                            .all(|((ks, vs), (ko, vo))| (ks == ko) && (vs == vo))
320                });
321                if !in_pass {
322                    return Err(Error::FailValidate("Map is not on `in` list".to_string()));
323                }
324            }
325
326            let nin_pass = !self.nin_list.iter().any(|v| {
327                v.len() == map.len()
328                    && v.iter()
329                        .zip(map.iter())
330                        .all(|((ks, vs), (ko, vo))| (ks == ko) && (vs == vo))
331            });
332            if !nin_pass {
333                return Err(Error::FailValidate("Map is on `nin` list".to_string()));
334            }
335        }
336
337        // Loop through each item, verifying it with the appropriate validator
338        let mut reqs_found = 0;
339        let mut array_len: Option<usize> = None;
340        let mut array_len_cnt = 0;
341        for _ in 0..len {
342            // Extract the key
343            let elem = parser
344                .next()
345                .ok_or_else(|| Error::FailValidate("expected a key string".to_string()))??;
346            let key = if let Element::Str(v) = elem {
347                v
348            } else {
349                return Err(Error::FailValidate(format!(
350                    "expected Str, got {}",
351                    elem.name()
352                )));
353            };
354
355            if self.same_len.iter().any(|s| s == key) {
356                // Peek the array and its length
357                let elem = parser.peek().ok_or_else(|| {
358                    Error::FailValidate("expected an array element".to_string())
359                })??;
360                let Element::Array(len) = elem else {
361                    return Err(Error::FailValidate(format!(
362                        "expected array for key {:?}, got {}",
363                        key, elem.name()
364                    )));
365                };
366                if let Some(array_len) = array_len {
367                    if array_len != len {
368                        return Err(Error::FailValidate(format!(
369                            "expected array of length {} for key {:?}, but length was {}",
370                            array_len, key, len
371                        )));
372                    }
373                } else {
374                    array_len = Some(len);
375                }
376                array_len_cnt += 1;
377            }
378
379            // Look up the appropriate validator and use it
380            let (p, c) = if let Some(validator) = self.req.get(key) {
381                reqs_found += 1;
382                validator.validate(types, parser, checklist)?
383            } else if let Some(validator) = self.opt.get(key) {
384                validator.validate(types, parser, checklist)?
385            } else if let Some(validator) = &self.values {
386                // Make sure the key is valid before proceeding
387                if let Some(keys) = &self.keys {
388                    keys.validate_str(key)?;
389                }
390                validator.validate(types, parser, checklist)?
391            } else {
392                return Err(Error::FailValidate(format!(
393                    "Map key {:?} has no corresponding validator",
394                    key
395                )));
396            };
397
398            parser = p;
399            checklist = c;
400        }
401
402        if array_len.is_some() && array_len_cnt != self.same_len.len() {
403            return Err(Error::FailValidate(
404                "Map had some, but not all, of the keys listed in `same_len`".into(),
405            ));
406        }
407
408        if reqs_found != self.req.len() {
409            return Err(Error::FailValidate(format!(
410                "Map did not have all required key-value pairs (missing {})",
411                reqs_found
412            )));
413        }
414
415        Ok((parser, checklist))
416    }
417
418    fn query_check_self(&self, types: &BTreeMap<String, Validator>, other: &MapValidator) -> bool {
419        let initial_check = (self.query || (other.in_list.is_empty() && other.nin_list.is_empty()))
420            && (self.size || (u32_is_max(&other.max_len) && u32_is_zero(&other.min_len)))
421            && (self.same_len_ok || other.same_len.is_empty())
422            && (self.map_ok
423                || (other.req.is_empty()
424                    && other.opt.is_empty()
425                    && other.keys.is_none()
426                    && other.values.is_none()));
427        if !initial_check {
428            return false;
429        }
430        if self.map_ok {
431            // Make sure `keys` and `values` are OK, then check the req/opt pairs against matching
432            // validators
433
434            let values_ok = match (&self.values, &other.values) {
435                (None, None) => true,
436                (Some(_), None) => true,
437                (None, Some(_)) => false,
438                (Some(s), Some(o)) => s.query_check(types, o.as_ref()),
439            };
440            if !values_ok {
441                return false;
442            }
443
444            let keys_ok = match (&self.keys, &other.keys) {
445                (None, None) => true,
446                (Some(_), None) => true,
447                (None, Some(_)) => false,
448                (Some(s), Some(o)) => s.query_check_str(o.as_ref()),
449            };
450            if !keys_ok {
451                return false;
452            }
453
454            let req_ok = other.req.iter().all(|(ko, kv)| {
455                self.req
456                    .get(ko)
457                    .or_else(|| self.opt.get(ko))
458                    .or(self.values.as_deref())
459                    .map(|v| v.query_check(types, kv))
460                    .unwrap_or(false)
461            });
462            if !req_ok {
463                return false;
464            }
465
466            let opt_ok = other.opt.iter().all(|(ko, kv)| {
467                self.req
468                    .get(ko)
469                    .or_else(|| self.opt.get(ko))
470                    .or(self.values.as_deref())
471                    .map(|v| v.query_check(types, kv))
472                    .unwrap_or(false)
473            });
474            opt_ok
475        } else {
476            true
477        }
478    }
479
480    pub(crate) fn query_check(
481        &self,
482        types: &BTreeMap<String, Validator>,
483        other: &Validator,
484    ) -> bool {
485        match other {
486            Validator::Map(other) => self.query_check_self(types, other),
487            Validator::Multi(list) => list.iter().all(|other| match other {
488                Validator::Map(other) => self.query_check_self(types, other),
489                _ => false,
490            }),
491            Validator::Any => true,
492            _ => false,
493        }
494    }
495}
496
497#[cfg(test)]
498mod test {
499    use super::*;
500    use crate::{de::FogDeserializer, ser::FogSerializer};
501
502    #[test]
503    fn ser_default() {
504        // Should be an empty map if we use the defaults
505        let schema = MapValidator::default();
506        let mut ser = FogSerializer::default();
507        schema.serialize(&mut ser).unwrap();
508        let expected: Vec<u8> = vec![0x80];
509        let actual = ser.finish();
510        println!("expected: {:x?}", expected);
511        println!("actual:   {:x?}", actual);
512        assert_eq!(expected, actual);
513
514        let mut de = FogDeserializer::with_debug(&actual, "    ");
515        let decoded = MapValidator::deserialize(&mut de).unwrap();
516        println!("{}", de.get_debug().unwrap());
517        assert_eq!(schema, decoded);
518    }
519
520    #[test]
521    fn same_len() {
522        let schema = MapValidator::new()
523            .values(ArrayValidator::new().build())
524            .same_len_add("a")
525            .same_len_add("b");
526
527        #[derive(Clone, Debug, Serialize, Deserialize)]
528        struct Test {
529            #[serde(skip_serializing_if = "Vec::is_empty")]
530            a: Vec<u8>,
531            #[serde(skip_serializing_if = "Vec::is_empty")]
532            b: Vec<u8>,
533        }
534
535        // Passing
536        let test = Test {
537            a: vec![0, 1],
538            b: vec![2, 3],
539        };
540        let mut ser = FogSerializer::default();
541        test.serialize(&mut ser).unwrap();
542        let serialized = ser.finish();
543        let parser = Parser::new(&serialized);
544        assert!(schema.validate(&BTreeMap::new(), parser, None).is_ok());
545
546        // Passing by empty
547        let test = Test {
548            a: vec![],
549            b: vec![],
550        };
551        let mut ser = FogSerializer::default();
552        test.serialize(&mut ser).unwrap();
553        let serialized = ser.finish();
554        let parser = Parser::new(&serialized);
555        assert!(schema.validate(&BTreeMap::new(), parser, None).is_ok());
556
557        // Failing with only one present
558        let test = Test {
559            a: vec![2, 3],
560            b: vec![],
561        };
562        let mut ser = FogSerializer::default();
563        test.serialize(&mut ser).unwrap();
564        let serialized = ser.finish();
565        let parser = Parser::new(&serialized);
566        assert!(schema.validate(&BTreeMap::new(), parser, None).is_err());
567
568        // Failing with only both present but incorrect lengths
569        let test = Test {
570            a: vec![2, 3],
571            b: vec![1, 2, 3],
572        };
573        let mut ser = FogSerializer::default();
574        test.serialize(&mut ser).unwrap();
575        let serialized = ser.finish();
576        let parser = Parser::new(&serialized);
577        assert!(schema.validate(&BTreeMap::new(), parser, None).is_err());
578    }
579}