use crate::compiler::{build_substitution_group_map, SubstitutionGroupMap};
use crate::schema::SchemaSet;
use super::errors::ValidationError;
use super::info::ValidationFlags;
use super::runtime::ValidationRuntime;
pub trait ValidationSink {
fn on_error(&mut self, error: ValidationError);
fn on_warning(&mut self, warning: ValidationWarning);
}
#[derive(Debug, Clone)]
pub struct ValidationWarning {
pub code: &'static str,
pub message: String,
pub location: Option<crate::parser::location::SourceLocation>,
}
impl std::fmt::Display for ValidationWarning {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[{}] {}", self.code, self.message)?;
if let Some(loc) = &self.location {
write!(f, " at {}", loc)?;
}
Ok(())
}
}
pub struct CollectingValidationSink<'a> {
pub errors: &'a mut Vec<ValidationError>,
pub warnings: &'a mut Vec<ValidationWarning>,
}
impl<'a> ValidationSink for CollectingValidationSink<'a> {
fn on_error(&mut self, error: ValidationError) {
self.errors.push(error);
}
fn on_warning(&mut self, warning: ValidationWarning) {
self.warnings.push(warning);
}
}
pub struct ErrorOnlySink<'a> {
pub errors: &'a mut Vec<ValidationError>,
}
impl<'a> ValidationSink for ErrorOnlySink<'a> {
fn on_error(&mut self, error: ValidationError) {
self.errors.push(error);
}
fn on_warning(&mut self, _warning: ValidationWarning) {
}
}
#[cfg(feature = "xsd11")]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum AssertionSource {
#[default]
Disabled,
FragmentBuffer,
MainDocument,
}
pub struct SchemaValidator<'a> {
pub(crate) schema_set: &'a SchemaSet,
pub(crate) subst_groups: Option<SubstitutionGroupMap>,
pub(crate) flags: ValidationFlags,
#[cfg(feature = "xsd11")]
pub(crate) assertion_source: AssertionSource,
}
impl<'a> SchemaValidator<'a> {
pub fn new(schema_set: &'a SchemaSet, flags: ValidationFlags) -> Self {
#[cfg(feature = "xsd11")]
let flags = flags & !ValidationFlags::PROCESS_ASSERTIONS;
let subst_groups = build_substitution_group_map(schema_set);
SchemaValidator {
schema_set,
subst_groups: Some(subst_groups),
flags,
#[cfg(feature = "xsd11")]
assertion_source: AssertionSource::default(),
}
}
pub fn with_substitution_groups(
schema_set: &'a SchemaSet,
flags: ValidationFlags,
subst_groups: SubstitutionGroupMap,
) -> Self {
SchemaValidator {
subst_groups: Some(subst_groups),
..Self::new(schema_set, flags)
}
}
#[cfg(feature = "xsd11")]
pub fn new_fragment_buffer(schema_set: &'a SchemaSet, flags: ValidationFlags) -> Self {
let mut v = Self::new(schema_set, flags);
v.flags |= ValidationFlags::PROCESS_ASSERTIONS;
v.assertion_source = AssertionSource::FragmentBuffer;
v
}
#[cfg(feature = "xsd11")]
pub fn new_main_document(schema_set: &'a SchemaSet, flags: ValidationFlags) -> Self {
let flags = flags & !ValidationFlags::PROCESS_ASSERTIONS;
let mut v = Self::new(schema_set, flags);
v.assertion_source = AssertionSource::MainDocument;
v
}
#[cfg(feature = "xsd11")]
#[allow(dead_code)] pub(crate) fn set_assertion_source(&mut self, source: AssertionSource) -> &mut Self {
let has_flag = self.flags.contains(ValidationFlags::PROCESS_ASSERTIONS);
match source {
AssertionSource::FragmentBuffer => {
assert!(
has_flag,
"AssertionSource::FragmentBuffer requires ValidationFlags::PROCESS_ASSERTIONS"
);
}
AssertionSource::Disabled | AssertionSource::MainDocument => {
assert!(
!has_flag,
"AssertionSource::{:?} requires PROCESS_ASSERTIONS to NOT be set",
source
);
}
}
self.assertion_source = source;
self
}
pub fn start_run<S: ValidationSink>(&self, sink: S) -> ValidationRuntime<'_, S> {
ValidationRuntime::new(
self.schema_set,
&self.subst_groups,
self.flags,
sink,
#[cfg(feature = "xsd11")]
self.assertion_source,
)
}
}
#[cfg(test)]
#[cfg(feature = "xsd11")]
mod assertion_source_tests {
use super::*;
use crate::pipeline::load_and_process_schema;
fn load_schema(xsd: &str) -> SchemaSet {
let mut schema_set = SchemaSet::new();
load_and_process_schema(xsd.as_bytes(), "test.xsd", &mut schema_set, None)
.expect("failed to load schema");
schema_set
}
#[test]
fn test_assertion_source_default_is_disabled() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let v = SchemaValidator::new(&schema_set, ValidationFlags::default());
assert_eq!(v.assertion_source, AssertionSource::Disabled);
}
#[test]
fn test_fragment_buffer_constructor() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let v = SchemaValidator::new_fragment_buffer(&schema_set, ValidationFlags::default());
assert_eq!(v.assertion_source, AssertionSource::FragmentBuffer);
assert!(v.flags.contains(ValidationFlags::PROCESS_ASSERTIONS));
}
#[test]
fn test_new_strips_process_assertions_flag() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let flags = ValidationFlags::default() | ValidationFlags::PROCESS_ASSERTIONS;
let v = SchemaValidator::new(&schema_set, flags);
assert!(!v.flags.contains(ValidationFlags::PROCESS_ASSERTIONS));
assert_eq!(v.assertion_source, AssertionSource::Disabled);
}
#[test]
#[should_panic(expected = "PROCESS_ASSERTIONS")]
fn test_fragment_buffer_without_flag_panics() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let mut v = SchemaValidator::new(&schema_set, ValidationFlags::default());
v.set_assertion_source(AssertionSource::FragmentBuffer);
}
#[test]
#[should_panic(expected = "PROCESS_ASSERTIONS")]
fn test_disabled_with_flag_panics() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let mut v = SchemaValidator::new_fragment_buffer(&schema_set, ValidationFlags::default());
v.set_assertion_source(AssertionSource::Disabled);
}
#[test]
#[should_panic(expected = "PROCESS_ASSERTIONS")]
fn test_main_document_with_flag_panics() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let mut v = SchemaValidator::new_fragment_buffer(&schema_set, ValidationFlags::default());
v.set_assertion_source(AssertionSource::MainDocument);
}
#[test]
fn test_main_document_without_flag_ok() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let mut v = SchemaValidator::new(&schema_set, ValidationFlags::default());
v.set_assertion_source(AssertionSource::MainDocument);
assert_eq!(v.assertion_source, AssertionSource::MainDocument);
}
}