yaml_schema/validation/
one_of.rs

1use log::{debug, error};
2
3use super::Validator;
4
5use crate::Context;
6use crate::Error;
7use crate::Result;
8use crate::YamlSchema;
9impl Validator for crate::schemas::OneOfSchema {
10    fn validate(&self, context: &Context, value: &saphyr::MarkedYaml) -> Result<()> {
11        let one_of_is_valid = validate_one_of(context, &self.one_of, value)?;
12        if !one_of_is_valid {
13            error!("OneOf: None of the schemas in `oneOf` matched!");
14            context.add_error(value, "None of the schemas in `oneOf` matched!");
15            fail_fast!(context);
16        }
17        Ok(())
18    }
19}
20
21pub fn validate_one_of(
22    context: &Context,
23    schemas: &Vec<YamlSchema>,
24    value: &saphyr::MarkedYaml,
25) -> Result<bool> {
26    let mut one_of_is_valid = false;
27    for schema in schemas {
28        debug!(
29            "[OneOf] Validating value: {:?} against schema: {}",
30            &value.data, schema
31        );
32        let sub_context = context.get_sub_context();
33        let sub_result = schema.validate(&sub_context, value);
34        match sub_result {
35            Ok(()) | Err(Error::FailFast) => {
36                debug!(
37                    "[OneOf] sub_context.errors: {}",
38                    sub_context.errors.borrow().len()
39                );
40                if sub_context.has_errors() {
41                    continue;
42                }
43
44                if one_of_is_valid {
45                    error!("[OneOf] Value matched multiple schemas in `oneOf`!");
46                    context.add_error(value, "Value matched multiple schemas in `oneOf`!");
47                    fail_fast!(context);
48                } else {
49                    one_of_is_valid = true;
50                }
51            }
52            Err(e) => return Err(e),
53        }
54    }
55    debug!("OneOf: one_of_is_valid: {one_of_is_valid}");
56    Ok(one_of_is_valid)
57}
58
59#[cfg(test)]
60mod tests {
61    use crate::RootSchema;
62    use crate::Schema;
63    use saphyr::LoadableYamlNode;
64
65    #[test]
66    fn test_validate_one_of_with_array_of_schemas() {
67        let root_schema = RootSchema::load_from_str(
68            r##"
69            $defs:
70              schema:
71                type: object
72                properties:
73                  type:
74                    enum: [string, object, number, integer, boolean, enum, array, oneOf, anyOf, not]
75              array_of_schemas:
76                type: array
77                items:
78                  $ref: "#/$defs/schema"
79            oneOf:
80              - type: boolean
81              - $ref: "#/$defs/array_of_schemas"
82            "##,
83        )
84        .unwrap();
85        println!("root_schema: {root_schema:#?}");
86        let root_schema_schema = root_schema.schema.as_ref().schema.as_ref().unwrap();
87        if let Schema::OneOf(one_of_schema) = root_schema_schema {
88            println!("one_of_schema: {one_of_schema:#?}");
89        } else {
90            panic!("Expected Schema::OneOf, but got: {root_schema_schema:?}");
91        }
92
93        let s = r#"
94            false
95            "#;
96        let docs = saphyr::MarkedYaml::load_from_str(s).unwrap();
97        let value = docs.first().unwrap();
98        let context = crate::Context::with_root_schema(&root_schema, false);
99        let result = root_schema.validate(&context, value);
100        println!("result: {result:#?}");
101        assert!(result.is_ok());
102        for error in context.errors.borrow().iter() {
103            println!("error: {error:#?}");
104        }
105        assert!(!context.has_errors());
106    }
107}