xsd_schema/validation/
validator.rs1use crate::compiler::{build_substitution_group_map, SubstitutionGroupMap};
10use crate::schema::SchemaSet;
11
12use super::errors::ValidationError;
13use super::info::ValidationFlags;
14use super::runtime::ValidationRuntime;
15
16pub trait ValidationSink {
24 fn on_error(&mut self, error: ValidationError);
26 fn on_warning(&mut self, warning: ValidationWarning);
28}
29
30#[derive(Debug, Clone)]
32pub struct ValidationWarning {
33 pub code: &'static str,
35 pub message: String,
37 pub location: Option<crate::parser::location::SourceLocation>,
39}
40
41impl std::fmt::Display for ValidationWarning {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 write!(f, "[{}] {}", self.code, self.message)?;
44 if let Some(loc) = &self.location {
45 write!(f, " at {}", loc)?;
46 }
47 Ok(())
48 }
49}
50
51pub struct CollectingValidationSink<'a> {
57 pub errors: &'a mut Vec<ValidationError>,
58 pub warnings: &'a mut Vec<ValidationWarning>,
59}
60
61impl<'a> ValidationSink for CollectingValidationSink<'a> {
62 fn on_error(&mut self, error: ValidationError) {
63 self.errors.push(error);
64 }
65 fn on_warning(&mut self, warning: ValidationWarning) {
66 self.warnings.push(warning);
67 }
68}
69
70pub struct ErrorOnlySink<'a> {
72 pub errors: &'a mut Vec<ValidationError>,
73}
74
75impl<'a> ValidationSink for ErrorOnlySink<'a> {
76 fn on_error(&mut self, error: ValidationError) {
77 self.errors.push(error);
78 }
79 fn on_warning(&mut self, _warning: ValidationWarning) {
80 }
82}
83
84#[cfg(feature = "xsd11")]
96#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
97pub enum AssertionSource {
98 #[default]
100 Disabled,
101 FragmentBuffer,
103 MainDocument,
105}
106
107pub struct SchemaValidator<'a> {
116 pub(crate) schema_set: &'a SchemaSet,
118 pub(crate) subst_groups: Option<SubstitutionGroupMap>,
120 pub(crate) flags: ValidationFlags,
122 #[cfg(feature = "xsd11")]
124 pub(crate) assertion_source: AssertionSource,
125}
126
127impl<'a> SchemaValidator<'a> {
128 pub fn new(schema_set: &'a SchemaSet, flags: ValidationFlags) -> Self {
135 #[cfg(feature = "xsd11")]
136 let flags = flags & !ValidationFlags::PROCESS_ASSERTIONS;
137 let subst_groups = build_substitution_group_map(schema_set);
138 SchemaValidator {
139 schema_set,
140 subst_groups: Some(subst_groups),
141 flags,
142 #[cfg(feature = "xsd11")]
143 assertion_source: AssertionSource::default(),
144 }
145 }
146
147 pub fn with_substitution_groups(
149 schema_set: &'a SchemaSet,
150 flags: ValidationFlags,
151 subst_groups: SubstitutionGroupMap,
152 ) -> Self {
153 SchemaValidator {
154 subst_groups: Some(subst_groups),
155 ..Self::new(schema_set, flags)
156 }
157 }
158
159 #[cfg(feature = "xsd11")]
161 pub fn new_fragment_buffer(schema_set: &'a SchemaSet, flags: ValidationFlags) -> Self {
162 let mut v = Self::new(schema_set, flags);
163 v.flags |= ValidationFlags::PROCESS_ASSERTIONS;
164 v.assertion_source = AssertionSource::FragmentBuffer;
165 v
166 }
167
168 #[cfg(feature = "xsd11")]
170 pub fn new_main_document(schema_set: &'a SchemaSet, flags: ValidationFlags) -> Self {
171 let flags = flags & !ValidationFlags::PROCESS_ASSERTIONS;
172 let mut v = Self::new(schema_set, flags);
173 v.assertion_source = AssertionSource::MainDocument;
174 v
175 }
176
177 #[cfg(feature = "xsd11")]
187 #[allow(dead_code)] pub(crate) fn set_assertion_source(&mut self, source: AssertionSource) -> &mut Self {
189 let has_flag = self.flags.contains(ValidationFlags::PROCESS_ASSERTIONS);
190 match source {
191 AssertionSource::FragmentBuffer => {
192 assert!(
193 has_flag,
194 "AssertionSource::FragmentBuffer requires ValidationFlags::PROCESS_ASSERTIONS"
195 );
196 }
197 AssertionSource::Disabled | AssertionSource::MainDocument => {
198 assert!(
199 !has_flag,
200 "AssertionSource::{:?} requires PROCESS_ASSERTIONS to NOT be set",
201 source
202 );
203 }
204 }
205 self.assertion_source = source;
206 self
207 }
208
209 pub fn start_run<S: ValidationSink>(&self, sink: S) -> ValidationRuntime<'_, S> {
211 ValidationRuntime::new(
212 self.schema_set,
213 &self.subst_groups,
214 self.flags,
215 sink,
216 #[cfg(feature = "xsd11")]
217 self.assertion_source,
218 )
219 }
220}
221
222#[cfg(test)]
227#[cfg(feature = "xsd11")]
228mod assertion_source_tests {
229 use super::*;
230 use crate::pipeline::load_and_process_schema;
231
232 fn load_schema(xsd: &str) -> SchemaSet {
233 let mut schema_set = SchemaSet::new();
234 load_and_process_schema(xsd.as_bytes(), "test.xsd", &mut schema_set, None)
235 .expect("failed to load schema");
236 schema_set
237 }
238
239 #[test]
240 fn test_assertion_source_default_is_disabled() {
241 let schema_set = load_schema(
242 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
243 <xs:element name="root" type="xs:string"/>
244 </xs:schema>"#,
245 );
246 let v = SchemaValidator::new(&schema_set, ValidationFlags::default());
247 assert_eq!(v.assertion_source, AssertionSource::Disabled);
248 }
249
250 #[test]
251 fn test_fragment_buffer_constructor() {
252 let schema_set = load_schema(
253 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
254 <xs:element name="root" type="xs:string"/>
255 </xs:schema>"#,
256 );
257 let v = SchemaValidator::new_fragment_buffer(&schema_set, ValidationFlags::default());
258 assert_eq!(v.assertion_source, AssertionSource::FragmentBuffer);
259 assert!(v.flags.contains(ValidationFlags::PROCESS_ASSERTIONS));
260 }
261
262 #[test]
263 fn test_new_strips_process_assertions_flag() {
264 let schema_set = load_schema(
265 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
266 <xs:element name="root" type="xs:string"/>
267 </xs:schema>"#,
268 );
269 let flags = ValidationFlags::default() | ValidationFlags::PROCESS_ASSERTIONS;
271 let v = SchemaValidator::new(&schema_set, flags);
272 assert!(!v.flags.contains(ValidationFlags::PROCESS_ASSERTIONS));
273 assert_eq!(v.assertion_source, AssertionSource::Disabled);
274 }
275
276 #[test]
277 #[should_panic(expected = "PROCESS_ASSERTIONS")]
278 fn test_fragment_buffer_without_flag_panics() {
279 let schema_set = load_schema(
280 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
281 <xs:element name="root" type="xs:string"/>
282 </xs:schema>"#,
283 );
284 let mut v = SchemaValidator::new(&schema_set, ValidationFlags::default());
285 v.set_assertion_source(AssertionSource::FragmentBuffer);
286 }
287
288 #[test]
289 #[should_panic(expected = "PROCESS_ASSERTIONS")]
290 fn test_disabled_with_flag_panics() {
291 let schema_set = load_schema(
292 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
293 <xs:element name="root" type="xs:string"/>
294 </xs:schema>"#,
295 );
296 let mut v = SchemaValidator::new_fragment_buffer(&schema_set, ValidationFlags::default());
299 v.set_assertion_source(AssertionSource::Disabled);
300 }
301
302 #[test]
303 #[should_panic(expected = "PROCESS_ASSERTIONS")]
304 fn test_main_document_with_flag_panics() {
305 let schema_set = load_schema(
306 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
307 <xs:element name="root" type="xs:string"/>
308 </xs:schema>"#,
309 );
310 let mut v = SchemaValidator::new_fragment_buffer(&schema_set, ValidationFlags::default());
313 v.set_assertion_source(AssertionSource::MainDocument);
314 }
315
316 #[test]
317 fn test_main_document_without_flag_ok() {
318 let schema_set = load_schema(
319 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
320 <xs:element name="root" type="xs:string"/>
321 </xs:schema>"#,
322 );
323 let mut v = SchemaValidator::new(&schema_set, ValidationFlags::default());
324 v.set_assertion_source(AssertionSource::MainDocument);
325 assert_eq!(v.assertion_source, AssertionSource::MainDocument);
326 }
327}