fog_pack/validator/
multi.rs

1use super::*;
2use crate::error::{Error, Result};
3use serde::{Deserialize, Serialize};
4use std::default::Default;
5
6/// "Multi" validator that checks with several validators at once.
7///
8/// This validator will accept any value that passes at least one of its contained validators. This
9/// can be used like an "any of" operator, or a logical OR of validators.
10///
11/// When this validator is used, the contained validators are checked in order, passing when the
12/// first contained validator passes. When performing [`Entry`][crate::entry::Entry] validation, this can mean that a
13/// linked document may be added to the list of documents needed for final validation, even if
14/// another contained validator (later in the list) would also pass without it.
15///
16/// When going through the contained validators, some rules are followed to avoid possible cyclic
17/// references:
18///
19/// - Contained Multi-validators are skipped
20/// - Contained Ref validators that refer to a Multi-validator are skipped.
21/// - Contained Ref validators that refer to a Ref validator are skipped.
22///
23/// More succintly, the banned sequences are: Multi->Multi, Multi->Ref->Multi, Multi->Ref->Ref.
24///
25/// # Query Checking
26///
27/// The validator for a query must be accepted by at least one of the validators in the
28/// Multi-validator. Contained validators that violate the cyclic reference rules are skipped (see
29/// above).
30///
31#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
32pub struct MultiValidator(pub Vec<Validator>);
33
34impl MultiValidator {
35    /// Make a new validator with the default configuration.
36    pub fn new() -> Self {
37        Self::default()
38    }
39
40    /// Add a new validator to the list.
41    pub fn push(mut self, validator: Validator) -> Self {
42        self.0.push(validator);
43        self
44    }
45
46    /// Build this into a [`Validator`] enum.
47    pub fn build(self) -> Validator {
48        Validator::Multi(self)
49    }
50
51    /// Iterate over all the contained validators.
52    pub fn iter(&self) -> std::slice::Iter<Validator> {
53        self.0.iter()
54    }
55
56    pub(crate) fn validate<'de, 'c>(
57        &'c self,
58        types: &'c BTreeMap<String, Validator>,
59        parser: Parser<'de>,
60        checklist: Option<Checklist<'c>>,
61    ) -> Result<(Parser<'de>, Option<Checklist<'c>>)> {
62        // Iterate through Multi list, but skip any validators that could potentially be
63        // cyclic. Banned: Multi->Multi, Multi->Ref->Multi, Multi->Ref->Ref.
64        for validator in self.0.iter() {
65            let new_parser = parser.clone();
66            let new_checklist = checklist.clone();
67            let new_result = match validator {
68                Validator::Ref(ref_name) => match types.get(ref_name) {
69                    None => continue,
70                    Some(validator) => match validator {
71                        Validator::Ref(_) => continue,
72                        Validator::Multi(_) => continue,
73                        _ => validator.validate(types, new_parser, new_checklist),
74                    },
75                },
76                Validator::Multi(_) => {
77                    continue;
78                }
79                _ => validator.validate(types, new_parser, new_checklist),
80            };
81            // We clone the parser each time because the validator modifies its state while
82            // processing. On a pass, we return the parser state that passed
83            if new_result.is_ok() {
84                return new_result;
85            }
86        }
87        Err(Error::FailValidate(
88            "validator Multi had no passing validators".to_string(),
89        ))
90    }
91
92    pub(crate) fn query_check(
93        &self,
94        types: &BTreeMap<String, Validator>,
95        other: &Validator,
96    ) -> bool {
97        self.0.iter().any(|validator| match validator {
98            Validator::Ref(ref_name) => match types.get(ref_name) {
99                None => false,
100                Some(validator) => match validator {
101                    Validator::Ref(_) => false,
102                    Validator::Multi(_) => false,
103                    _ => validator.query_check(types, other),
104                },
105            },
106            Validator::Multi(_) => false,
107            _ => validator.query_check(types, other),
108        })
109    }
110}