polyxml 0.30.6__tar.gz → 0.31.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {polyxml-0.30.6 → polyxml-0.31.0}/Cargo.lock +6 -6
- {polyxml-0.30.6 → polyxml-0.31.0}/Cargo.toml +1 -1
- {polyxml-0.30.6 → polyxml-0.31.0}/PKG-INFO +1 -1
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/src/ir/mod.rs +318 -1
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/src/schema_parser/mod.rs +24 -5
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/tests/test_schema_ir.rs +56 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/pyproject.toml +1 -1
- {polyxml-0.30.6 → polyxml-0.31.0}/README.md +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/Cargo.toml +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/README.md +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/benches/core_benchmarks.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/benches/support/tag_dispatch_fixtures.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/benches/tag_dispatch.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/src/codegen/cpp/mod.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/src/codegen/csharp/mod.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/src/codegen/go/mod.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/src/codegen/java/codec.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/src/codegen/java/mod.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/src/codegen/java/models.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/src/codegen/mod.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/src/codegen/python/aot.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/src/codegen/python/mod.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/src/codegen/rust/mod.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/src/codegen/typescript/mod.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/src/converters.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/src/error.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/src/ir/tarjan.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/src/json.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/src/lib.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/src/parser.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/src/schema.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/src/serializer.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/src/transcoder.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/src/value.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/tests/fixtures/lexical_union.xsd +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/tests/fixtures/mixed_content.xsd +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/tests/fixtures/ts_mixed_content_inheritance.xsd +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/tests/test_adversarial_identifiers.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/tests/test_conformance.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/tests/test_core.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/tests/test_cpp_codegen.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/tests/test_csharp_codegen.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/tests/test_go_codegen.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/tests/test_java_codegen.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/tests/test_json.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/tests/test_lexical_union_codegen.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/tests/test_mixed_content.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/tests/test_pattern_codegen.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/tests/test_python_codegen.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/tests/test_rust_codecs.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/tests/test_rust_codegen.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/tests/test_schema_audit.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/tests/test_security.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/tests/test_transcoder.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/tests/test_ts_codegen.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/tests/test_unbounded_choice.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/tests/test_xsi_type.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-python/Cargo.toml +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-python/README.md +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-python/benches/aot_vs_dataclass.py +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-python/src/lib.rs +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-python/tests/test_conformance.py +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-python/tests/test_generated_models.py +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-python/tests/test_json.py +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-python/tests/test_mixed_content.py +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-python/tests/test_polyxml.py +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-python/tests/test_xsi_type.py +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/python/polyxml/AGENT_GUIDE.md +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/python/polyxml/__init__.py +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/python/polyxml/compat/__init__.py +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/python/polyxml/compat/xsdata.py +0 -0
- {polyxml-0.30.6 → polyxml-0.31.0}/python/polyxml/py.typed +0 -0
|
@@ -608,7 +608,7 @@ dependencies = [
|
|
|
608
608
|
|
|
609
609
|
[[package]]
|
|
610
610
|
name = "polyxml"
|
|
611
|
-
version = "0.
|
|
611
|
+
version = "0.31.0"
|
|
612
612
|
dependencies = [
|
|
613
613
|
"criterion",
|
|
614
614
|
"heck",
|
|
@@ -629,14 +629,14 @@ dependencies = [
|
|
|
629
629
|
|
|
630
630
|
[[package]]
|
|
631
631
|
name = "polyxml-c"
|
|
632
|
-
version = "0.
|
|
632
|
+
version = "0.31.0"
|
|
633
633
|
dependencies = [
|
|
634
634
|
"polyxml",
|
|
635
635
|
]
|
|
636
636
|
|
|
637
637
|
[[package]]
|
|
638
638
|
name = "polyxml-cli"
|
|
639
|
-
version = "0.
|
|
639
|
+
version = "0.31.0"
|
|
640
640
|
dependencies = [
|
|
641
641
|
"clap",
|
|
642
642
|
"glob",
|
|
@@ -650,7 +650,7 @@ dependencies = [
|
|
|
650
650
|
|
|
651
651
|
[[package]]
|
|
652
652
|
name = "polyxml-js"
|
|
653
|
-
version = "0.
|
|
653
|
+
version = "0.31.0"
|
|
654
654
|
dependencies = [
|
|
655
655
|
"napi",
|
|
656
656
|
"napi-build",
|
|
@@ -660,7 +660,7 @@ dependencies = [
|
|
|
660
660
|
|
|
661
661
|
[[package]]
|
|
662
662
|
name = "polyxml-python"
|
|
663
|
-
version = "0.
|
|
663
|
+
version = "0.31.0"
|
|
664
664
|
dependencies = [
|
|
665
665
|
"lexical-core",
|
|
666
666
|
"polyxml",
|
|
@@ -670,7 +670,7 @@ dependencies = [
|
|
|
670
670
|
|
|
671
671
|
[[package]]
|
|
672
672
|
name = "polyxml-wasm"
|
|
673
|
-
version = "0.
|
|
673
|
+
version = "0.31.0"
|
|
674
674
|
dependencies = [
|
|
675
675
|
"polyxml",
|
|
676
676
|
"quick-xml",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
pub mod tarjan;
|
|
2
2
|
|
|
3
3
|
use serde::{Deserialize, Serialize};
|
|
4
|
-
use std::collections::{BTreeMap, HashMap};
|
|
4
|
+
use std::collections::{BTreeMap, BTreeSet, HashMap};
|
|
5
5
|
use std::fmt;
|
|
6
6
|
|
|
7
7
|
/// Fully qualified XML Name (Namespace URI + Local Name).
|
|
@@ -467,6 +467,161 @@ pub struct SchemaIR {
|
|
|
467
467
|
}
|
|
468
468
|
|
|
469
469
|
impl SchemaIR {
|
|
470
|
+
/// Keep the selected global elements and all types that can occur beneath them.
|
|
471
|
+
/// The schema is parsed in full before this operation, so references can be
|
|
472
|
+
/// followed across imports and includes.
|
|
473
|
+
pub fn select_root_elements(&self, roots: &[String]) -> Result<Self, String> {
|
|
474
|
+
if roots.is_empty() {
|
|
475
|
+
return Ok(self.clone());
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
let mut selected_elements = BTreeSet::new();
|
|
479
|
+
for name in roots {
|
|
480
|
+
let matches: Vec<_> = if let Some(qualified) = name.strip_prefix('{') {
|
|
481
|
+
let (namespace, local) = qualified.split_once('}').ok_or_else(|| {
|
|
482
|
+
format!("Invalid root element '{name}': expected {{namespace}}local-name")
|
|
483
|
+
})?;
|
|
484
|
+
if local.is_empty() {
|
|
485
|
+
return Err(format!("Invalid root element '{name}': missing local name"));
|
|
486
|
+
}
|
|
487
|
+
self.elements
|
|
488
|
+
.keys()
|
|
489
|
+
.filter(|q| {
|
|
490
|
+
q.namespace.as_deref().unwrap_or("") == namespace && q.local == local
|
|
491
|
+
})
|
|
492
|
+
.cloned()
|
|
493
|
+
.collect()
|
|
494
|
+
} else {
|
|
495
|
+
self.elements
|
|
496
|
+
.keys()
|
|
497
|
+
.filter(|q| q.local == *name)
|
|
498
|
+
.cloned()
|
|
499
|
+
.collect()
|
|
500
|
+
};
|
|
501
|
+
match matches.as_slice() {
|
|
502
|
+
[] => return Err(format!("Root element '{name}' was not found in the schema")),
|
|
503
|
+
[qname] => {
|
|
504
|
+
selected_elements.insert(qname.clone());
|
|
505
|
+
}
|
|
506
|
+
_ => {
|
|
507
|
+
return Err(format!(
|
|
508
|
+
"Root element '{name}' is ambiguous; use {{namespace}}local-name"
|
|
509
|
+
))
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
let mut descendants: HashMap<QName, Vec<QName>> = HashMap::new();
|
|
515
|
+
for (qname, def) in &self.types {
|
|
516
|
+
let base = match def {
|
|
517
|
+
TypeDef::Struct(s) => s.base_type.as_ref(),
|
|
518
|
+
TypeDef::Simple(s) => named_ref(&s.base_type),
|
|
519
|
+
TypeDef::Enum(e) => named_ref(&e.base_type),
|
|
520
|
+
TypeDef::Union(_) => None,
|
|
521
|
+
};
|
|
522
|
+
if let Some(base) = base {
|
|
523
|
+
descendants
|
|
524
|
+
.entry(base.clone())
|
|
525
|
+
.or_default()
|
|
526
|
+
.push(qname.clone());
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
let mut selected_types = BTreeSet::new();
|
|
531
|
+
let mut pending_elements: Vec<_> = selected_elements.iter().cloned().collect();
|
|
532
|
+
let mut pending_types: Vec<(QName, bool)> = Vec::new();
|
|
533
|
+
let mut expanded_polymorphic = BTreeSet::new();
|
|
534
|
+
while !pending_elements.is_empty() || !pending_types.is_empty() {
|
|
535
|
+
while let Some(qname) = pending_elements.pop() {
|
|
536
|
+
if let Some(element) = self.elements.get(&qname) {
|
|
537
|
+
push_named_ref(&element.type_ref, true, &mut pending_types);
|
|
538
|
+
if let Some(head) = &element.substitution_group {
|
|
539
|
+
if selected_elements.insert(head.clone()) {
|
|
540
|
+
pending_elements.push(head.clone());
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
if let Some(members) = self.substitution_groups.get(&qname) {
|
|
545
|
+
for member in members {
|
|
546
|
+
if selected_elements.insert(member.clone()) {
|
|
547
|
+
pending_elements.push(member.clone());
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
if let Some((qname, polymorphic)) = pending_types.pop() {
|
|
553
|
+
if polymorphic && expanded_polymorphic.insert(qname.clone()) {
|
|
554
|
+
if let Some(children) = descendants.get(&qname) {
|
|
555
|
+
pending_types.extend(children.iter().cloned().map(|child| (child, true)));
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
if !selected_types.insert(qname.clone()) {
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
if let Some(def) = self.types.get(&qname) {
|
|
562
|
+
match def {
|
|
563
|
+
TypeDef::Struct(s) => {
|
|
564
|
+
if let Some(base) = &s.base_type {
|
|
565
|
+
pending_types.push((base.clone(), false));
|
|
566
|
+
}
|
|
567
|
+
for field in &s.fields {
|
|
568
|
+
push_named_ref(&field.type_ref, true, &mut pending_types);
|
|
569
|
+
if field.kind == FieldKind::Element {
|
|
570
|
+
let element = referenced_element_name(
|
|
571
|
+
field.namespace.as_deref(),
|
|
572
|
+
&field.xml_name,
|
|
573
|
+
);
|
|
574
|
+
if self.substitution_groups.contains_key(&element)
|
|
575
|
+
&& selected_elements.insert(element.clone())
|
|
576
|
+
{
|
|
577
|
+
pending_elements.push(element);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
TypeDef::Simple(s) => {
|
|
583
|
+
push_named_ref(&s.base_type, false, &mut pending_types)
|
|
584
|
+
}
|
|
585
|
+
TypeDef::Enum(e) => push_named_ref(&e.base_type, false, &mut pending_types),
|
|
586
|
+
TypeDef::Union(u) => {
|
|
587
|
+
for branch in &u.branches {
|
|
588
|
+
push_named_ref(&branch.type_ref, true, &mut pending_types);
|
|
589
|
+
let element = referenced_element_name(
|
|
590
|
+
branch.namespace.as_deref(),
|
|
591
|
+
&branch.xml_name,
|
|
592
|
+
);
|
|
593
|
+
if self.substitution_groups.contains_key(&element)
|
|
594
|
+
&& selected_elements.insert(element.clone())
|
|
595
|
+
{
|
|
596
|
+
pending_elements.push(element);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
let mut result = self.clone();
|
|
606
|
+
result
|
|
607
|
+
.types
|
|
608
|
+
.retain(|qname, _| selected_types.contains(qname));
|
|
609
|
+
result
|
|
610
|
+
.elements
|
|
611
|
+
.retain(|qname, _| selected_elements.contains(qname));
|
|
612
|
+
result
|
|
613
|
+
.external_types
|
|
614
|
+
.retain(|qname, _| selected_types.contains(qname));
|
|
615
|
+
result.substitution_groups.retain(|head, members| {
|
|
616
|
+
if !selected_elements.contains(head) {
|
|
617
|
+
return false;
|
|
618
|
+
}
|
|
619
|
+
members.retain(|member| selected_elements.contains(member));
|
|
620
|
+
!members.is_empty()
|
|
621
|
+
});
|
|
622
|
+
Ok(result)
|
|
623
|
+
}
|
|
624
|
+
|
|
470
625
|
pub fn is_external_type(&self, qname: &QName) -> bool {
|
|
471
626
|
if qname
|
|
472
627
|
.namespace
|
|
@@ -520,3 +675,165 @@ impl SchemaIR {
|
|
|
520
675
|
tarjan::resolve_cycles(self);
|
|
521
676
|
}
|
|
522
677
|
}
|
|
678
|
+
|
|
679
|
+
fn named_ref(type_ref: &TypeRef) -> Option<&QName> {
|
|
680
|
+
match type_ref {
|
|
681
|
+
TypeRef::Named(qname) => Some(qname),
|
|
682
|
+
TypeRef::Boxed(inner) | TypeRef::List(inner) => named_ref(inner),
|
|
683
|
+
TypeRef::Primitive(_) => None,
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
fn push_named_ref(type_ref: &TypeRef, polymorphic: bool, pending: &mut Vec<(QName, bool)>) {
|
|
688
|
+
if let Some(qname) = named_ref(type_ref) {
|
|
689
|
+
pending.push((qname.clone(), polymorphic));
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
fn referenced_element_name(namespace: Option<&str>, xml_name: &str) -> QName {
|
|
694
|
+
let local = xml_name.rsplit(':').next().unwrap_or(xml_name);
|
|
695
|
+
QName::new(namespace, local)
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
#[cfg(test)]
|
|
699
|
+
mod root_selection_tests {
|
|
700
|
+
use super::*;
|
|
701
|
+
|
|
702
|
+
fn structure(name: &str, base: Option<&str>, fields: Vec<FieldDef>) -> TypeDef {
|
|
703
|
+
TypeDef::Struct(StructDef {
|
|
704
|
+
qname: QName::new(Some("urn:test"), name),
|
|
705
|
+
base_type: base.map(|name| QName::new(Some("urn:test"), name)),
|
|
706
|
+
is_abstract: false,
|
|
707
|
+
is_mixed: false,
|
|
708
|
+
fields,
|
|
709
|
+
documentation: None,
|
|
710
|
+
})
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
fn element(name: &str, ty: &str) -> ElementDef {
|
|
714
|
+
ElementDef {
|
|
715
|
+
qname: QName::new(Some("urn:test"), name),
|
|
716
|
+
type_ref: TypeRef::named(QName::new(Some("urn:test"), ty)),
|
|
717
|
+
substitution_group: None,
|
|
718
|
+
nillable: false,
|
|
719
|
+
documentation: None,
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
#[test]
|
|
724
|
+
fn keeps_references_descendants_and_substitutions() {
|
|
725
|
+
let mut ir = SchemaIR::new();
|
|
726
|
+
let mut field = FieldDef::new(
|
|
727
|
+
"item",
|
|
728
|
+
"Item",
|
|
729
|
+
FieldKind::Element,
|
|
730
|
+
TypeRef::List(Box::new(TypeRef::named(QName::new(
|
|
731
|
+
Some("urn:test"),
|
|
732
|
+
"Choice",
|
|
733
|
+
)))),
|
|
734
|
+
);
|
|
735
|
+
field.namespace = Some("urn:test".to_string());
|
|
736
|
+
ir.add_type(structure("RootType", None, vec![field]));
|
|
737
|
+
ir.add_type(structure("Base", None, vec![]));
|
|
738
|
+
ir.add_type(structure("Derived", Some("Base"), vec![]));
|
|
739
|
+
ir.add_type(TypeDef::Union(UnionDef {
|
|
740
|
+
qname: QName::new(Some("urn:test"), "Choice"),
|
|
741
|
+
branches: vec![UnionBranch {
|
|
742
|
+
variant_name: "Base".into(),
|
|
743
|
+
xml_name: "Base".into(),
|
|
744
|
+
namespace: Some("urn:test".into()),
|
|
745
|
+
type_ref: TypeRef::named(QName::new(Some("urn:test"), "Base")),
|
|
746
|
+
documentation: None,
|
|
747
|
+
}],
|
|
748
|
+
documentation: None,
|
|
749
|
+
}));
|
|
750
|
+
ir.add_type(structure("Unrelated", None, vec![]));
|
|
751
|
+
ir.add_element(element("Root", "RootType"));
|
|
752
|
+
ir.add_element(element("UnrelatedRoot", "Unrelated"));
|
|
753
|
+
let mut member = element("Member", "Derived");
|
|
754
|
+
member.substitution_group = Some(QName::new(Some("urn:test"), "Root"));
|
|
755
|
+
ir.add_element(member);
|
|
756
|
+
|
|
757
|
+
let selected = ir.select_root_elements(&["Root".into()]).unwrap();
|
|
758
|
+
assert_eq!(selected.elements.len(), 2);
|
|
759
|
+
assert_eq!(selected.types.len(), 4);
|
|
760
|
+
assert!(selected
|
|
761
|
+
.types
|
|
762
|
+
.contains_key(&QName::new(Some("urn:test"), "Derived")));
|
|
763
|
+
assert!(!selected
|
|
764
|
+
.types
|
|
765
|
+
.contains_key(&QName::new(Some("urn:test"), "Unrelated")));
|
|
766
|
+
let combined = ir
|
|
767
|
+
.select_root_elements(&["Root".into(), "UnrelatedRoot".into()])
|
|
768
|
+
.unwrap();
|
|
769
|
+
assert_eq!(combined.elements.len(), 3);
|
|
770
|
+
assert!(combined
|
|
771
|
+
.types
|
|
772
|
+
.contains_key(&QName::new(Some("urn:test"), "Unrelated")));
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
#[test]
|
|
776
|
+
fn rejects_missing_and_ambiguous_roots() {
|
|
777
|
+
let mut ir = SchemaIR::new();
|
|
778
|
+
ir.add_element(element("Root", "RootType"));
|
|
779
|
+
let mut second = element("Root", "RootType");
|
|
780
|
+
second.qname.namespace = Some("urn:other".into());
|
|
781
|
+
ir.add_element(second);
|
|
782
|
+
assert!(ir
|
|
783
|
+
.select_root_elements(&["Root".into()])
|
|
784
|
+
.unwrap_err()
|
|
785
|
+
.contains("ambiguous"));
|
|
786
|
+
assert_eq!(
|
|
787
|
+
ir.select_root_elements(&["{urn:test}Root".into()])
|
|
788
|
+
.unwrap()
|
|
789
|
+
.elements
|
|
790
|
+
.len(),
|
|
791
|
+
1
|
|
792
|
+
);
|
|
793
|
+
assert!(ir
|
|
794
|
+
.select_root_elements(&["Missing".into()])
|
|
795
|
+
.unwrap_err()
|
|
796
|
+
.contains("not found"));
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
#[test]
|
|
800
|
+
fn inherited_base_does_not_select_sibling_types() {
|
|
801
|
+
let mut ir = SchemaIR::new();
|
|
802
|
+
ir.add_type(structure("CommonBase", None, vec![]));
|
|
803
|
+
ir.add_type(structure("SelectedType", Some("CommonBase"), vec![]));
|
|
804
|
+
ir.add_type(structure("SiblingType", Some("CommonBase"), vec![]));
|
|
805
|
+
ir.add_element(element("Selected", "SelectedType"));
|
|
806
|
+
let selected = ir.select_root_elements(&["Selected".into()]).unwrap();
|
|
807
|
+
assert!(selected
|
|
808
|
+
.types
|
|
809
|
+
.contains_key(&QName::new(Some("urn:test"), "CommonBase")));
|
|
810
|
+
assert!(selected
|
|
811
|
+
.types
|
|
812
|
+
.contains_key(&QName::new(Some("urn:test"), "SelectedType")));
|
|
813
|
+
assert!(!selected
|
|
814
|
+
.types
|
|
815
|
+
.contains_key(&QName::new(Some("urn:test"), "SiblingType")));
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
#[test]
|
|
819
|
+
fn field_reference_keeps_substitution_group_members() {
|
|
820
|
+
let xsd = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
|
821
|
+
xmlns:t="urn:test" targetNamespace="urn:test" elementFormDefault="qualified">
|
|
822
|
+
<xs:element name="Head" type="xs:string" abstract="true"/>
|
|
823
|
+
<xs:element name="Member" type="xs:string" substitutionGroup="t:Head"/>
|
|
824
|
+
<xs:complexType name="ContainerType"><xs:sequence>
|
|
825
|
+
<xs:element ref="t:Head"/>
|
|
826
|
+
</xs:sequence></xs:complexType>
|
|
827
|
+
<xs:element name="Container" type="t:ContainerType"/>
|
|
828
|
+
</xs:schema>"#;
|
|
829
|
+
let mut parser = crate::schema_parser::XsdParser::new();
|
|
830
|
+
let ir = parser.parse_str(xsd).unwrap();
|
|
831
|
+
let selected = ir.select_root_elements(&["Container".into()]).unwrap();
|
|
832
|
+
assert!(selected
|
|
833
|
+
.elements
|
|
834
|
+
.contains_key(&QName::new(Some("urn:test"), "Head")));
|
|
835
|
+
assert!(selected
|
|
836
|
+
.elements
|
|
837
|
+
.contains_key(&QName::new(Some("urn:test"), "Member")));
|
|
838
|
+
}
|
|
839
|
+
}
|
|
@@ -189,7 +189,12 @@ impl XsdParser {
|
|
|
189
189
|
|
|
190
190
|
let mut ir = SchemaIR::new();
|
|
191
191
|
let mut target_namespace = None;
|
|
192
|
-
|
|
192
|
+
// The `xml` prefix is bound by XML itself, even when the schema omits
|
|
193
|
+
// an explicit xmlns:xml declaration (as Dublin Core's dc.xsd does).
|
|
194
|
+
let mut prefixes = HashMap::from([(
|
|
195
|
+
"xml".to_string(),
|
|
196
|
+
"http://www.w3.org/XML/1998/namespace".to_string(),
|
|
197
|
+
)]);
|
|
193
198
|
let mut buf = Vec::new();
|
|
194
199
|
|
|
195
200
|
// Pass 1: Parse root schema attributes and build prefix table
|
|
@@ -698,7 +703,10 @@ impl XsdParser {
|
|
|
698
703
|
if frame.kind == CompositorKind::Choice {
|
|
699
704
|
let choice_is_unbounded = frame.is_unbounded
|
|
700
705
|
|| compositor_stack.iter().any(|c| c.is_unbounded);
|
|
701
|
-
if choice_is_unbounded
|
|
706
|
+
if (choice_is_unbounded
|
|
707
|
+
|| (compositor_stack.is_empty() && base_type.is_some()))
|
|
708
|
+
&& !frame.choice_branches.is_empty()
|
|
709
|
+
{
|
|
702
710
|
let choice_name =
|
|
703
711
|
unique_type_name(ir, target_ns, &format!("{}Choice", name));
|
|
704
712
|
let choice_qname = QName::new(target_ns, choice_name);
|
|
@@ -710,10 +718,15 @@ impl XsdParser {
|
|
|
710
718
|
ir.add_type(TypeDef::Union(choice_def));
|
|
711
719
|
|
|
712
720
|
fields.truncate(frame.fields_start);
|
|
713
|
-
let
|
|
721
|
+
let field_base = if choice_is_unbounded {
|
|
722
|
+
"items"
|
|
723
|
+
} else {
|
|
724
|
+
"choice"
|
|
725
|
+
};
|
|
726
|
+
let mut item_field_name = field_base.to_string();
|
|
714
727
|
let mut counter = 2;
|
|
715
728
|
while fields.iter().any(|f| f.name == item_field_name) {
|
|
716
|
-
item_field_name = format!("
|
|
729
|
+
item_field_name = format!("{field_base}_{counter}");
|
|
717
730
|
counter += 1;
|
|
718
731
|
}
|
|
719
732
|
fields.push(FieldDef {
|
|
@@ -722,7 +735,13 @@ impl XsdParser {
|
|
|
722
735
|
namespace: None,
|
|
723
736
|
kind: FieldKind::Element,
|
|
724
737
|
type_ref: TypeRef::Named(choice_qname),
|
|
725
|
-
cardinality:
|
|
738
|
+
cardinality: if choice_is_unbounded {
|
|
739
|
+
Cardinality::unbounded(frame.min_occurs)
|
|
740
|
+
} else if frame.min_occurs == 0 {
|
|
741
|
+
Cardinality::optional_one()
|
|
742
|
+
} else {
|
|
743
|
+
Cardinality::required_one()
|
|
744
|
+
},
|
|
726
745
|
nillable: false,
|
|
727
746
|
default_value: None,
|
|
728
747
|
fixed_value: None,
|
|
@@ -92,6 +92,62 @@ fn global_attribute_reference_uses_declared_type() {
|
|
|
92
92
|
);
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
#[test]
|
|
96
|
+
fn xml_prefix_is_available_without_explicit_declaration() {
|
|
97
|
+
let dir = tempdir().unwrap();
|
|
98
|
+
std::fs::write(
|
|
99
|
+
dir.path().join("xml.xsd"),
|
|
100
|
+
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.w3.org/XML/1998/namespace">
|
|
101
|
+
<xs:attribute name="lang" type="xs:language"/>
|
|
102
|
+
</xs:schema>"#,
|
|
103
|
+
)
|
|
104
|
+
.unwrap();
|
|
105
|
+
let main = dir.path().join("dc.xsd");
|
|
106
|
+
std::fs::write(
|
|
107
|
+
&main,
|
|
108
|
+
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:dc">
|
|
109
|
+
<xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="xml.xsd"/>
|
|
110
|
+
<xs:complexType name="SimpleLiteral"><xs:attribute ref="xml:lang"/></xs:complexType>
|
|
111
|
+
</xs:schema>"#,
|
|
112
|
+
)
|
|
113
|
+
.unwrap();
|
|
114
|
+
let ir = XsdParser::new().parse_file(&main).unwrap();
|
|
115
|
+
let TypeDef::Struct(literal) = &ir.types[&QName::new(Some("urn:dc"), "SimpleLiteral")] else {
|
|
116
|
+
panic!("expected SimpleLiteral struct");
|
|
117
|
+
};
|
|
118
|
+
assert_eq!(
|
|
119
|
+
literal.fields[0].type_ref,
|
|
120
|
+
TypeRef::Primitive(PrimitiveType::Language)
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
#[test]
|
|
125
|
+
fn derived_complex_type_keeps_base_and_bounded_choice() {
|
|
126
|
+
let xsd = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:t="urn:test" targetNamespace="urn:test">
|
|
127
|
+
<xs:complexType name="Base"><xs:attribute name="id" type="xs:string"/></xs:complexType>
|
|
128
|
+
<xs:complexType name="Node"><xs:complexContent><xs:extension base="t:Base"><xs:choice>
|
|
129
|
+
<xs:element name="left" type="xs:string"/><xs:element name="right" type="xs:int"/>
|
|
130
|
+
</xs:choice></xs:extension></xs:complexContent></xs:complexType>
|
|
131
|
+
<xs:complexType name="Child"><xs:complexContent><xs:extension base="t:Node">
|
|
132
|
+
<xs:attribute name="flag" type="xs:boolean"/>
|
|
133
|
+
</xs:extension></xs:complexContent></xs:complexType>
|
|
134
|
+
</xs:schema>"#;
|
|
135
|
+
let ir = XsdParser::new().parse_str(xsd).unwrap();
|
|
136
|
+
let TypeDef::Struct(node) = &ir.types[&QName::new(Some("urn:test"), "Node")] else {
|
|
137
|
+
panic!("derived choice must remain a struct");
|
|
138
|
+
};
|
|
139
|
+
assert_eq!(node.base_type, Some(QName::new(Some("urn:test"), "Base")));
|
|
140
|
+
assert_eq!(node.fields.len(), 1);
|
|
141
|
+
assert_eq!(node.fields[0].name, "choice");
|
|
142
|
+
let TypeRef::Named(choice_name) = &node.fields[0].type_ref else {
|
|
143
|
+
panic!("choice field must refer to a union");
|
|
144
|
+
};
|
|
145
|
+
let TypeDef::Union(choice) = &ir.types[choice_name] else {
|
|
146
|
+
panic!("choice field must refer to a union");
|
|
147
|
+
};
|
|
148
|
+
assert_eq!(choice.branches.len(), 2);
|
|
149
|
+
}
|
|
150
|
+
|
|
95
151
|
#[test]
|
|
96
152
|
fn test_parse_lexical_union_member_types_and_inline_members() {
|
|
97
153
|
let xsd = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:t="urn:test" targetNamespace="urn:test">
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{polyxml-0.30.6 → polyxml-0.31.0}/crates/polyxml-core/benches/support/tag_dispatch_fixtures.rs
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|