1use crate::error::SchemaResult;
31use crate::ids::DocumentId;
32use crate::parser::parse::{parse_schema_with_config, ParserConfig};
33#[cfg(feature = "async")]
34use crate::parser::resolver::resolve_all_directives_async;
35use crate::parser::resolver::{
36 fixup_composition_edges, resolve_all_directives, ResolutionResult, ResolverConfig,
37 SchemaResolver,
38};
39use crate::schema::{
40 allocate_content_particle_elements, allocate_model_group_particle_elements,
41 assemble_inline_types, build_dependency_graph, compile_all_patterns, resolve_all_references,
42 validate_all_derivations, validate_attribute_id_constraints,
43 validate_attribute_value_constraints, validate_element_value_constraints, InlineAssemblyStats,
44 ResolutionStats,
45};
46use crate::SchemaSet;
47
48#[derive(Debug, Clone)]
50pub struct PipelineConfig {
51 pub parser: ParserConfig,
53 pub resolver: ResolverConfig,
55 pub resolve_directives: bool,
59 pub assemble_inline_types: bool,
61 pub resolve_references: bool,
63}
64
65impl Default for PipelineConfig {
66 fn default() -> Self {
67 Self {
68 parser: ParserConfig::default(),
69 resolver: ResolverConfig::default(),
70 resolve_directives: true,
71 assemble_inline_types: true,
72 resolve_references: true,
73 }
74 }
75}
76
77impl PipelineConfig {
78 pub fn parse_only() -> Self {
80 Self {
81 parser: ParserConfig::default(),
82 resolver: ResolverConfig::default(),
83 resolve_directives: false,
84 assemble_inline_types: false,
85 resolve_references: false,
86 }
87 }
88
89 pub fn full() -> Self {
91 Self::default()
92 }
93}
94
95#[derive(Debug, Default)]
97pub struct PipelineStats {
98 pub doc_id: DocumentId,
100 pub loaded_docs: Vec<DocumentId>,
102 pub directive_result: Option<DirectiveStats>,
104 pub inline_stats: Option<InlineAssemblyStats>,
106 pub resolution_stats: Option<ResolutionStats>,
108}
109
110#[derive(Debug, Default)]
112pub struct DirectiveStats {
113 pub loaded_count: usize,
115 pub skipped_count: usize,
117 pub error_count: usize,
119}
120
121impl From<&ResolutionResult> for DirectiveStats {
122 fn from(result: &ResolutionResult) -> Self {
123 Self {
124 loaded_count: result.loaded.len(),
125 skipped_count: result.skipped.len(),
126 error_count: result.errors.len() + result.import_errors.len(),
127 }
128 }
129}
130
131pub fn load_and_process_schema(
175 xml: &[u8],
176 base_uri: &str,
177 schema_set: &mut SchemaSet,
178 config: Option<PipelineConfig>,
179) -> SchemaResult<PipelineStats> {
180 let config = config.unwrap_or_default();
181 let mut stats = PipelineStats::default();
182
183 let doc_id = parse_schema_with_config(xml, base_uri, schema_set, &config.parser)?;
185 stats.doc_id = doc_id;
186
187 if config.resolve_directives {
189 let mut resolver = SchemaResolver::with_config(config.resolver.clone());
190
191 let dir_result = resolve_all_directives(doc_id, &mut resolver, schema_set);
193
194 stats.loaded_docs.extend(dir_result.loaded.iter().copied());
196 stats.directive_result = Some(DirectiveStats::from(&dir_result));
197 let mut directive_errors: Vec<crate::error::SchemaError> = dir_result.errors;
198 let mut import_errors: Vec<crate::error::SchemaError> = dir_result.import_errors;
199
200 let mut pending_docs = dir_result.loaded.clone();
202 while !pending_docs.is_empty() {
203 let current_batch: Vec<_> = std::mem::take(&mut pending_docs);
204 for loaded_doc_id in current_batch {
205 let nested_result =
206 resolve_all_directives(loaded_doc_id, &mut resolver, schema_set);
207 stats
208 .loaded_docs
209 .extend(nested_result.loaded.iter().copied());
210 pending_docs.extend(nested_result.loaded.iter().copied());
211
212 if let Some(ref mut dir_stats) = stats.directive_result {
214 dir_stats.loaded_count += nested_result.loaded.len();
215 dir_stats.skipped_count += nested_result.skipped.len();
216 dir_stats.error_count +=
217 nested_result.errors.len() + nested_result.import_errors.len();
218 }
219 directive_errors.extend(nested_result.errors);
220 import_errors.extend(nested_result.import_errors);
221 }
222 }
223
224 fixup_composition_edges(schema_set);
226
227 if let Some(err) = directive_errors
230 .into_iter()
231 .chain(import_errors)
232 .find(|e| e.is_schema_content_error())
233 {
234 return Err(err);
235 }
236 }
237
238 if !schema_set.parsing_errors.is_empty() {
240 let errors = std::mem::take(&mut schema_set.parsing_errors);
241 return Err(errors.into_iter().next().unwrap());
242 }
243
244 if config.assemble_inline_types || config.resolve_references {
248 crate::schema::apply_redefine_override(schema_set)?;
249 }
250
251 if config.assemble_inline_types {
253 let inline_stats = assemble_inline_types(schema_set)?;
254 stats.inline_stats = Some(inline_stats);
255 }
256
257 if config.resolve_references {
259 let resolution_stats = resolve_all_references(schema_set)?;
260 stats.resolution_stats = Some(resolution_stats);
261 }
262
263 if config.resolve_references {
265 compile_all_patterns(schema_set)?;
266 }
267
268 #[cfg(feature = "xsd11")]
270 if config.resolve_references {
271 crate::compiler::validate_all_default_open_content(schema_set)?;
272 }
273
274 if config.resolve_references {
276 let (dep_graph, _dep_stats) = build_dependency_graph(schema_set)?;
277 validate_all_derivations(schema_set, &dep_graph)?;
278 }
279
280 if config.resolve_references {
283 validate_attribute_id_constraints(schema_set)?;
284 validate_attribute_value_constraints(schema_set)?;
285 validate_element_value_constraints(schema_set)?;
286 #[cfg(feature = "xsd11")]
287 xsd11_pre_resolution_validations(schema_set)?;
288 }
289
290 if config.resolve_references {
294 crate::schema::validate_xsd10_annotation_source_anyuri(schema_set)?;
295 }
296
297 if config.resolve_references {
301 crate::schema::validate_complex_type_attribute_uniqueness(schema_set)?;
302 }
303
304 if config.resolve_references {
306 crate::compiler::substitution::validate_all_substitution_groups(schema_set)?;
307 }
308
309 if config.assemble_inline_types && config.resolve_references {
311 allocate_content_particle_elements(schema_set)?;
312 allocate_model_group_particle_elements(schema_set)?;
313 finalize_local_element_pass(schema_set)?;
314 }
315
316 Ok(stats)
317}
318
319pub fn load_schema(
324 xml: &[u8],
325 base_uri: &str,
326 schema_set: &mut SchemaSet,
327) -> SchemaResult<PipelineStats> {
328 load_and_process_schema(xml, base_uri, schema_set, Some(PipelineConfig::full()))
329}
330
331pub fn parse_schema_only(
336 xml: &[u8],
337 base_uri: &str,
338 schema_set: &mut SchemaSet,
339) -> SchemaResult<DocumentId> {
340 let config = PipelineConfig::parse_only();
341 let stats = load_and_process_schema(xml, base_uri, schema_set, Some(config))?;
342 Ok(stats.doc_id)
343}
344
345pub fn process_loaded_schemas(
353 schema_set: &mut SchemaSet,
354) -> SchemaResult<(InlineAssemblyStats, ResolutionStats)> {
355 if !schema_set.parsing_errors.is_empty() {
357 let errors = std::mem::take(&mut schema_set.parsing_errors);
358 return Err(errors.into_iter().next().unwrap());
359 }
360
361 crate::schema::apply_redefine_override(schema_set)?;
363
364 let inline_stats = assemble_inline_types(schema_set)?;
365 let resolution_stats = resolve_all_references(schema_set)?;
366
367 compile_all_patterns(schema_set)?;
369
370 #[cfg(feature = "xsd11")]
372 crate::compiler::validate_all_default_open_content(schema_set)?;
373
374 let (dep_graph, _dep_stats) = build_dependency_graph(schema_set)?;
376 validate_all_derivations(schema_set, &dep_graph)?;
377
378 validate_attribute_id_constraints(schema_set)?;
380
381 validate_attribute_value_constraints(schema_set)?;
383
384 validate_element_value_constraints(schema_set)?;
386
387 #[cfg(feature = "xsd11")]
388 xsd11_pre_resolution_validations(schema_set)?;
389
390 crate::schema::validate_xsd10_annotation_source_anyuri(schema_set)?;
393
394 crate::schema::validate_complex_type_attribute_uniqueness(schema_set)?;
397
398 crate::schema::validate_local_decl_target_namespace(schema_set)?;
403
404 crate::schema::validate_no_xsi_attribute_declarations(schema_set)?;
407
408 crate::compiler::substitution::validate_all_substitution_groups(schema_set)?;
410
411 allocate_content_particle_elements(schema_set)?;
412 allocate_model_group_particle_elements(schema_set)?;
413
414 crate::schema::validate_substitution_group_element_consistency(schema_set)?;
420 finalize_local_element_pass(schema_set)?;
421 Ok((inline_stats, resolution_stats))
422}
423
424fn finalize_local_element_pass(schema_set: &mut SchemaSet) -> SchemaResult<()> {
430 #[cfg(feature = "xsd11")]
433 {
434 let new_alt_types = crate::schema::inline::resolve_local_element_alternatives(schema_set)?;
435 if !new_alt_types.is_empty() {
436 resolve_all_references(schema_set)?;
437 allocate_content_particle_elements(schema_set)?;
438 }
439 }
440 crate::schema::finalize_pending_ic_refs(schema_set)?;
444 validate_element_value_constraints(schema_set)?;
447 #[cfg(feature = "xsd11")]
448 {
449 crate::schema::validate_cta_xpath(schema_set)?;
450 crate::schema::validate_cta_substitutability(schema_set)?;
451 }
452 #[cfg(feature = "xsd11")]
453 xsd11_element_consistency_checks(schema_set)?;
454 validate_all_group_outer_occurs(schema_set)?;
455 validate_all_group_content(schema_set)?;
456 validate_all_group_placement(schema_set)?;
457 validate_all_particle_occurs(schema_set)?;
458 validate_all_upa_constraints(schema_set)?;
459 Ok(())
460}
461
462#[cfg(feature = "xsd11")]
469fn xsd11_pre_resolution_validations(schema_set: &SchemaSet) -> SchemaResult<()> {
470 crate::schema::validate_element_type_alternatives(schema_set)?;
472 crate::schema::validate_cta_xpath(schema_set)?;
475 crate::schema::validate_cta_substitutability(schema_set)?;
477 crate::schema::validate_wildcard_disallowed_names(schema_set)?;
480 Ok(())
481}
482
483#[cfg(feature = "xsd11")]
488fn xsd11_element_consistency_checks(schema_set: &SchemaSet) -> SchemaResult<()> {
489 crate::schema::validate_local_element_type_table_consistency(schema_set)?;
492 crate::schema::validate_wildcard_element_type_table_consistency(schema_set)?;
495 crate::schema::validate_restriction_local_element_type_table_consistency(schema_set)?;
498 Ok(())
499}
500
501fn validate_all_group_outer_occurs(schema_set: &SchemaSet) -> SchemaResult<()> {
507 use crate::compiler::{
508 is_top_level_all_group, resolve_top_level_all_group_ref, validate_outer_all_group_occurs,
509 };
510
511 for (_, type_def) in schema_set.arenas.complex_types.iter() {
512 let Some(particle) = (match &type_def.content {
513 crate::parser::frames::ComplexContentResult::Complex(content) => {
514 content.particle.as_ref()
515 }
516 crate::parser::frames::ComplexContentResult::Empty
517 | crate::parser::frames::ComplexContentResult::Simple(_) => None,
518 }) else {
519 continue;
520 };
521
522 let is_all = is_top_level_all_group(particle).is_some()
523 || resolve_top_level_all_group_ref(particle, schema_set).is_some();
524 if !is_all {
525 continue;
526 }
527
528 validate_outer_all_group_occurs(particle, schema_set.xsd_version).map_err(|error| {
529 let location = error
530 .location()
531 .and_then(|source| schema_set.source_maps.locate(source));
532 crate::error::SchemaError::structural("cos-all-limited", format!("{}", error), location)
533 })?;
534 }
535
536 Ok(())
537}
538
539fn validate_all_group_content(schema_set: &SchemaSet) -> SchemaResult<()> {
549 use crate::parser::frames::{ComplexContentResult, Compositor};
550
551 if !schema_set.is_xsd10() {
552 return Ok(());
553 }
554
555 for (_, mg) in schema_set.arenas.model_groups.iter() {
557 if mg.compositor == Some(Compositor::All) {
558 check_all_group_xsd10_constraints(&mg.particles, schema_set)?;
559 }
560 }
561
562 for (_, type_def) in schema_set.arenas.complex_types.iter() {
564 if let ComplexContentResult::Complex(content) = &type_def.content {
565 if let Some(particle) = content.particle.as_ref() {
566 check_particle_all_group_constraints(particle, schema_set)?;
567 }
568 }
569 }
570
571 Ok(())
572}
573
574fn check_all_group_xsd10_constraints(
575 particles: &[crate::parser::frames::ParticleResult],
576 schema_set: &SchemaSet,
577) -> SchemaResult<()> {
578 use crate::parser::frames::ParticleTerm;
579
580 for particle in particles {
581 let particle_loc = schema_set.locate(particle.source.as_ref());
582 match &particle.term {
583 ParticleTerm::Any(wc) => {
584 let location = schema_set
585 .locate(wc.source.as_ref())
586 .or_else(|| particle_loc.clone());
587 return Err(crate::error::SchemaError::structural(
588 "cos-all-limited",
589 "In XSD 1.0, xs:any (wildcard) is not allowed inside an xs:all group"
590 .to_string(),
591 location,
592 ));
593 }
594 ParticleTerm::Group(mg) => {
595 let location = schema_set
596 .locate(mg.source.as_ref())
597 .or_else(|| particle_loc.clone());
598 return Err(crate::error::SchemaError::structural(
599 "cos-all-limited",
600 "In XSD 1.0, a nested model group is not allowed inside an xs:all group"
601 .to_string(),
602 location,
603 ));
604 }
605 ParticleTerm::Element(_) => {}
606 }
607
608 if particle.min_occurs > 1 {
609 return Err(crate::error::SchemaError::structural(
610 "cos-all-limited",
611 format!(
612 "In XSD 1.0, an xs:all member's minOccurs must be 0 or 1, found {}",
613 particle.min_occurs
614 ),
615 particle_loc,
616 ));
617 }
618
619 match particle.max_occurs {
620 Some(0) | Some(1) => {}
621 Some(n) => {
622 return Err(crate::error::SchemaError::structural(
623 "cos-all-limited",
624 format!(
625 "In XSD 1.0, an xs:all member's maxOccurs must be 0 or 1, found {}",
626 n
627 ),
628 particle_loc,
629 ));
630 }
631 None => {
632 return Err(crate::error::SchemaError::structural(
633 "cos-all-limited",
634 "In XSD 1.0, an xs:all member's maxOccurs must be 0 or 1 (unbounded not allowed)"
635 .to_string(),
636 particle_loc,
637 ));
638 }
639 }
640 }
641 Ok(())
642}
643
644fn check_particle_all_group_constraints(
645 particle: &crate::parser::frames::ParticleResult,
646 schema_set: &SchemaSet,
647) -> SchemaResult<()> {
648 use crate::parser::frames::{Compositor, ParticleTerm};
649
650 if let ParticleTerm::Group(mg) = &particle.term {
651 if mg.compositor == Some(Compositor::All) {
652 check_all_group_xsd10_constraints(&mg.particles, schema_set)?;
653 }
654 for child in &mg.particles {
656 check_particle_all_group_constraints(child, schema_set)?;
657 }
658 }
659 Ok(())
660}
661
662fn validate_all_group_placement(schema_set: &SchemaSet) -> SchemaResult<()> {
668 use crate::compiler::{is_top_level_all_group, resolve_top_level_all_group_ref};
669 use crate::parser::frames::ComplexContentResult;
670
671 if !schema_set.is_xsd10() {
672 return Ok(());
673 }
674
675 for (_, type_def) in schema_set.arenas.complex_types.iter() {
676 let ComplexContentResult::Complex(content) = &type_def.content else {
677 continue;
678 };
679 let Some(particle) = content.particle.as_ref() else {
680 continue;
681 };
682
683 if is_top_level_all_group(particle).is_some()
687 || resolve_top_level_all_group_ref(particle, schema_set).is_some()
688 {
689 continue;
690 }
691
692 let mut visited = std::collections::HashSet::new();
693 check_no_nested_all_group(particle, schema_set, &mut visited)?;
694 }
695 Ok(())
696}
697
698fn check_no_nested_all_group(
699 particle: &crate::parser::frames::ParticleResult,
700 schema_set: &SchemaSet,
701 visited: &mut std::collections::HashSet<crate::ids::ModelGroupKey>,
702) -> SchemaResult<()> {
703 use crate::parser::frames::{Compositor, ParticleTerm, ParticleResult, ModelGroupDefResult};
704
705 fn placement_location(
706 particle: &ParticleResult,
707 group: &ModelGroupDefResult,
708 schema_set: &SchemaSet,
709 ) -> Option<crate::parser::location::SourceLocation> {
710 schema_set
711 .locate(particle.source.as_ref())
712 .or_else(|| schema_set.locate(group.source.as_ref()))
713 }
714
715 let ParticleTerm::Group(group) = &particle.term else {
716 return Ok(());
717 };
718
719 if group.compositor == Some(Compositor::All) && group.ref_name.is_none() {
720 return Err(crate::error::SchemaError::structural(
721 "cos-all-limited",
722 "In XSD 1.0, an xs:all model group may only appear as the top-level \
723 particle of a complex type definition"
724 .to_string(),
725 placement_location(particle, group, schema_set),
726 ));
727 }
728
729 if let Some(ref_name) = group.ref_name.as_ref() {
730 if let Some(group_key) =
731 schema_set.lookup_model_group(ref_name.namespace, ref_name.local_name)
732 {
733 if let Some(group_data) = schema_set.arenas.get_model_group(group_key) {
734 if group_data.compositor == Some(Compositor::All) {
735 return Err(crate::error::SchemaError::structural(
736 "cos-all-limited",
737 "In XSD 1.0, a reference to a named xs:all group may only \
738 appear as the top-level particle of a complex type definition"
739 .to_string(),
740 placement_location(particle, group, schema_set),
741 ));
742 }
743 if visited.insert(group_key) {
744 for child in &group_data.particles {
745 check_no_nested_all_group(child, schema_set, visited)?;
746 }
747 visited.remove(&group_key);
748 }
749 }
750 }
751 } else {
752 for child in &group.particles {
753 check_no_nested_all_group(child, schema_set, visited)?;
754 }
755 }
756 Ok(())
757}
758
759fn validate_all_particle_occurs(schema_set: &SchemaSet) -> SchemaResult<()> {
762 for (_, type_def) in schema_set.arenas.complex_types.iter() {
763 if let crate::parser::frames::ComplexContentResult::Complex(content) = &type_def.content {
764 if let Some(particle) = content.particle.as_ref() {
765 validate_particle_occurs_recursive(particle, schema_set)?;
766 }
767 }
768 }
769 for (_, mg) in schema_set.arenas.model_groups.iter() {
770 for particle in &mg.particles {
771 validate_particle_occurs_recursive(particle, schema_set)?;
772 }
773 }
774 Ok(())
775}
776
777fn validate_particle_occurs_recursive(
778 particle: &crate::parser::frames::ParticleResult,
779 schema_set: &SchemaSet,
780) -> SchemaResult<()> {
781 if let Some(max) = particle.max_occurs {
782 if particle.min_occurs > max {
783 let location = particle
784 .source
785 .as_ref()
786 .and_then(|s| schema_set.source_maps.locate(s));
787 return Err(crate::error::SchemaError::structural(
788 "p-props-correct",
789 format!(
790 "minOccurs ({}) exceeds maxOccurs ({})",
791 particle.min_occurs, max
792 ),
793 location,
794 ));
795 }
796 }
797 if let crate::parser::frames::ParticleTerm::Group(ref mg) = particle.term {
798 for child in &mg.particles {
799 validate_particle_occurs_recursive(child, schema_set)?;
800 }
801 }
802 Ok(())
803}
804
805fn validate_all_upa_constraints(schema_set: &SchemaSet) -> SchemaResult<()> {
806 for (_, type_def) in schema_set.arenas.complex_types.iter() {
807 if type_def.source.is_none() {
810 continue;
811 }
812
813 let Some(_particle) = (match &type_def.content {
814 crate::parser::frames::ComplexContentResult::Complex(content) => {
815 content.particle.as_ref()
816 }
817 crate::parser::frames::ComplexContentResult::Empty
818 | crate::parser::frames::ComplexContentResult::Simple(_) => None,
819 }) else {
820 continue;
821 };
822
823 let matcher = crate::compiler::compile_content_model_for_upa(schema_set, type_def)
826 .map_err(|error| {
827 let location = error
828 .location()
829 .and_then(|source| schema_set.source_maps.locate(source));
830 crate::error::SchemaError::structural(
831 "cos-nonambig",
832 format!(
833 "Failed to compile content model for UPA checking: {}",
834 error
835 ),
836 location,
837 )
838 })?;
839
840 match matcher {
841 crate::compiler::ContentModelMatcher::Nfa(nfa)
842 | crate::compiler::ContentModelMatcher::WithOpenContent { nfa, .. } => {
843 crate::compiler::check_upa(&nfa, schema_set, type_def.target_namespace)?;
844 }
845 crate::compiler::ContentModelMatcher::AllGroup(model) => {
846 crate::compiler::check_all_group_upa(
847 &model,
848 schema_set,
849 type_def.target_namespace,
850 )?;
851 }
852 #[cfg(feature = "xsd11")]
853 crate::compiler::ContentModelMatcher::AllGroupExtension {
854 base_model,
855 extension_nfa,
856 } => {
857 crate::compiler::check_all_group_upa(
858 &base_model,
859 schema_set,
860 type_def.target_namespace,
861 )?;
862 crate::compiler::check_upa(&extension_nfa, schema_set, type_def.target_namespace)?;
863 }
864 }
865 }
866
867 Ok(())
868}
869
870#[cfg(feature = "async")]
887pub async fn load_and_process_schema_async(
888 xml: &[u8],
889 base_uri: &str,
890 schema_set: &mut SchemaSet,
891 config: Option<PipelineConfig>,
892) -> SchemaResult<PipelineStats> {
893 let config = config.unwrap_or_default();
894 let mut stats = PipelineStats::default();
895
896 let doc_id = parse_schema_with_config(xml, base_uri, schema_set, &config.parser)?;
898 stats.doc_id = doc_id;
899
900 if config.resolve_directives {
902 let mut resolver = SchemaResolver::with_config(config.resolver.clone());
903
904 let dir_result = resolve_all_directives_async(doc_id, &mut resolver, schema_set).await;
905
906 stats.loaded_docs.extend(dir_result.loaded.iter().copied());
907 stats.directive_result = Some(DirectiveStats::from(&dir_result));
908 let mut directive_errors: Vec<crate::error::SchemaError> = dir_result.errors;
909 let mut import_errors: Vec<crate::error::SchemaError> = dir_result.import_errors;
910
911 let mut pending_docs = dir_result.loaded.clone();
913 while !pending_docs.is_empty() {
914 let current_batch: Vec<_> = std::mem::take(&mut pending_docs);
915 for loaded_doc_id in current_batch {
916 let nested_result =
917 resolve_all_directives_async(loaded_doc_id, &mut resolver, schema_set).await;
918 stats
919 .loaded_docs
920 .extend(nested_result.loaded.iter().copied());
921 pending_docs.extend(nested_result.loaded.iter().copied());
922
923 if let Some(ref mut dir_stats) = stats.directive_result {
924 dir_stats.loaded_count += nested_result.loaded.len();
925 dir_stats.skipped_count += nested_result.skipped.len();
926 dir_stats.error_count +=
927 nested_result.errors.len() + nested_result.import_errors.len();
928 }
929 directive_errors.extend(nested_result.errors);
930 import_errors.extend(nested_result.import_errors);
931 }
932 }
933
934 fixup_composition_edges(schema_set);
936
937 if let Some(err) = directive_errors
939 .into_iter()
940 .chain(import_errors)
941 .find(|e| e.is_schema_content_error())
942 {
943 return Err(err);
944 }
945 }
946
947 if config.assemble_inline_types || config.resolve_references {
949 crate::schema::apply_redefine_override(schema_set)?;
950 }
951
952 if config.assemble_inline_types {
954 let inline_stats = assemble_inline_types(schema_set)?;
955 stats.inline_stats = Some(inline_stats);
956 }
957
958 if config.resolve_references {
960 let resolution_stats = resolve_all_references(schema_set)?;
961 stats.resolution_stats = Some(resolution_stats);
962 }
963
964 if config.resolve_references {
966 compile_all_patterns(schema_set)?;
967 }
968
969 #[cfg(feature = "xsd11")]
971 if config.resolve_references {
972 crate::compiler::validate_all_default_open_content(schema_set)?;
973 }
974
975 if config.resolve_references {
977 let (dep_graph, _dep_stats) = build_dependency_graph(schema_set)?;
978 validate_all_derivations(schema_set, &dep_graph)?;
979 }
980
981 if config.resolve_references {
984 validate_attribute_id_constraints(schema_set)?;
985 validate_attribute_value_constraints(schema_set)?;
986 validate_element_value_constraints(schema_set)?;
987 #[cfg(feature = "xsd11")]
988 xsd11_pre_resolution_validations(schema_set)?;
989 }
990
991 if config.resolve_references {
995 crate::schema::validate_xsd10_annotation_source_anyuri(schema_set)?;
996 }
997
998 if config.resolve_references {
1002 crate::schema::validate_complex_type_attribute_uniqueness(schema_set)?;
1003 }
1004
1005 if config.assemble_inline_types && config.resolve_references {
1007 allocate_content_particle_elements(schema_set)?;
1008 allocate_model_group_particle_elements(schema_set)?;
1009 #[cfg(feature = "xsd11")]
1012 {
1013 let new_alt_types =
1014 crate::schema::inline::resolve_local_element_alternatives(schema_set)?;
1015 if !new_alt_types.is_empty() {
1016 resolve_all_references(schema_set)?;
1017 allocate_content_particle_elements(schema_set)?;
1018 }
1019 }
1020 #[cfg(feature = "xsd11")]
1021 xsd11_element_consistency_checks(schema_set)?;
1022 }
1023
1024 Ok(stats)
1025}
1026
1027#[cfg(feature = "async")]
1031pub async fn load_schema_async(
1032 xml: &[u8],
1033 base_uri: &str,
1034 schema_set: &mut SchemaSet,
1035) -> SchemaResult<PipelineStats> {
1036 load_and_process_schema_async(xml, base_uri, schema_set, Some(PipelineConfig::full())).await
1037}
1038
1039#[cfg(test)]
1040#[path = "pipeline_tests.rs"]
1041mod tests;