use crate::errors::ValidationError;
use crate::errors::{SchemaError, SchemaErrorKind};
use crate::utils::{CondenseErrors, YamlUtils};
use crate::{Context, PropertyType, Validate};
use std::convert::TryFrom;
use yaml_rust::Yaml;
#[derive(Debug)]
pub(crate) struct SchemaAnyOf<'schema> {
items: Vec<PropertyType<'schema>>,
}
impl<'schema> TryFrom<&'schema Yaml> for SchemaAnyOf<'schema> {
type Error = SchemaError<'schema>;
fn try_from(yaml: &'schema Yaml) -> Result<Self, Self::Error> {
yaml.strict_contents(&["anyOf"], &[])?;
let items = SchemaError::condense_errors(
&mut yaml
.lookup("anyOf", "array", Yaml::as_vec)?
.iter()
.map(|property| {
PropertyType::try_from(property).map_err(SchemaError::add_path_name("items"))
}),
)?;
if items.is_empty() {
return Err(SchemaErrorKind::MalformedField {
error: "anyOf modifier requires an array of schemas to validate against".to_owned(),
}
.with_path_name("anyOf"));
}
Ok(SchemaAnyOf { items })
}
}
impl<'yaml, 'schema: 'yaml> Validate<'yaml, 'schema> for SchemaAnyOf<'schema> {
fn validate(
&self,
ctx: &'schema Context<'schema>,
yaml: &'yaml Yaml,
) -> Result<(), ValidationError<'yaml>> {
let (valid, errs): (Vec<_>, Vec<_>) = self
.items
.iter()
.enumerate()
.map(|(_, schema)| schema.validate(ctx, yaml))
.partition(Result::is_ok);
if valid.is_empty() {
Err(ValidationError::condense_errors(&mut errs.into_iter()).unwrap_err())
} else {
Ok(())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utils::load_simple;
#[test]
fn one_of_from_yaml() {
SchemaAnyOf::try_from(&load_simple(
r#"
anyOf:
- type: integer
- type: string
"#,
))
.unwrap();
assert_eq!(
SchemaAnyOf::try_from(&load_simple(
r#"
anyOff:
- type: integer
"#,
))
.unwrap_err(),
SchemaErrorKind::Multiple {
errors: vec![
SchemaErrorKind::FieldMissing { field: "anyOf" }.into(),
SchemaErrorKind::ExtraField { field: "anyOff" }.into(),
]
}
.into()
)
}
#[test]
fn validate_unit_case() {
let yaml = load_simple(
r#"
anyOf:
- type: integer
"#,
);
let schema = SchemaAnyOf::try_from(&yaml).unwrap();
schema
.validate(&Context::default(), &load_simple("10"))
.unwrap();
}
#[test]
fn validate_multiple_valid() {
let yaml = load_simple(
r#"
anyOf:
- type: integer
- type: real
"#,
);
let schema = SchemaAnyOf::try_from(&yaml).unwrap();
schema
.validate(&Context::default(), &load_simple("10"))
.unwrap();
schema
.validate(&Context::default(), &load_simple("10.0"))
.unwrap();
}
#[test]
fn validate_complex_subvalidators() {
let yaml = load_simple(
r#"
anyOf:
- type: string
minLength: 10
- type: string
maxLength: 10
"#,
);
let schema = SchemaAnyOf::try_from(&yaml).unwrap();
schema
.validate(&Context::default(), &load_simple("hello you"))
.unwrap();
schema
.validate(&Context::default(), &load_simple("hello you"))
.unwrap();
schema
.validate(&Context::default(), &load_simple("hello world"))
.unwrap();
}
}