polyxml 0.30.3__tar.gz → 0.30.4__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.
Files changed (72) hide show
  1. {polyxml-0.30.3 → polyxml-0.30.4}/Cargo.lock +6 -6
  2. {polyxml-0.30.3 → polyxml-0.30.4}/Cargo.toml +1 -1
  3. {polyxml-0.30.3 → polyxml-0.30.4}/PKG-INFO +1 -1
  4. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/src/codegen/go/mod.rs +22 -3
  5. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/src/codegen/typescript/mod.rs +45 -10
  6. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/src/ir/mod.rs +3 -0
  7. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/src/schema_parser/mod.rs +109 -0
  8. polyxml-0.30.4/crates/polyxml-core/tests/fixtures/ts_mixed_content_inheritance.xsd +17 -0
  9. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/tests/test_go_codegen.rs +93 -0
  10. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/tests/test_schema_ir.rs +41 -0
  11. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/tests/test_ts_codegen.rs +29 -0
  12. {polyxml-0.30.3 → polyxml-0.30.4}/pyproject.toml +1 -1
  13. {polyxml-0.30.3 → polyxml-0.30.4}/README.md +0 -0
  14. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/Cargo.toml +0 -0
  15. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/README.md +0 -0
  16. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/benches/core_benchmarks.rs +0 -0
  17. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/benches/support/tag_dispatch_fixtures.rs +0 -0
  18. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/benches/tag_dispatch.rs +0 -0
  19. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/src/codegen/cpp/mod.rs +0 -0
  20. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/src/codegen/csharp/mod.rs +0 -0
  21. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/src/codegen/java/codec.rs +0 -0
  22. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/src/codegen/java/mod.rs +0 -0
  23. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/src/codegen/java/models.rs +0 -0
  24. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/src/codegen/mod.rs +0 -0
  25. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/src/codegen/python/aot.rs +0 -0
  26. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/src/codegen/python/mod.rs +0 -0
  27. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/src/codegen/rust/mod.rs +0 -0
  28. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/src/converters.rs +0 -0
  29. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/src/error.rs +0 -0
  30. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/src/ir/tarjan.rs +0 -0
  31. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/src/json.rs +0 -0
  32. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/src/lib.rs +0 -0
  33. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/src/parser.rs +0 -0
  34. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/src/schema.rs +0 -0
  35. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/src/serializer.rs +0 -0
  36. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/src/transcoder.rs +0 -0
  37. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/src/value.rs +0 -0
  38. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/tests/fixtures/lexical_union.xsd +0 -0
  39. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/tests/fixtures/mixed_content.xsd +0 -0
  40. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/tests/test_adversarial_identifiers.rs +0 -0
  41. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/tests/test_conformance.rs +0 -0
  42. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/tests/test_core.rs +0 -0
  43. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/tests/test_cpp_codegen.rs +0 -0
  44. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/tests/test_csharp_codegen.rs +0 -0
  45. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/tests/test_java_codegen.rs +0 -0
  46. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/tests/test_json.rs +0 -0
  47. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/tests/test_lexical_union_codegen.rs +0 -0
  48. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/tests/test_mixed_content.rs +0 -0
  49. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/tests/test_pattern_codegen.rs +0 -0
  50. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/tests/test_python_codegen.rs +0 -0
  51. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/tests/test_rust_codecs.rs +0 -0
  52. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/tests/test_rust_codegen.rs +0 -0
  53. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/tests/test_schema_audit.rs +0 -0
  54. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/tests/test_security.rs +0 -0
  55. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/tests/test_transcoder.rs +0 -0
  56. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/tests/test_unbounded_choice.rs +0 -0
  57. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-core/tests/test_xsi_type.rs +0 -0
  58. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-python/Cargo.toml +0 -0
  59. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-python/README.md +0 -0
  60. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-python/benches/aot_vs_dataclass.py +0 -0
  61. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-python/src/lib.rs +0 -0
  62. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-python/tests/test_conformance.py +0 -0
  63. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-python/tests/test_generated_models.py +0 -0
  64. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-python/tests/test_json.py +0 -0
  65. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-python/tests/test_mixed_content.py +0 -0
  66. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-python/tests/test_polyxml.py +0 -0
  67. {polyxml-0.30.3 → polyxml-0.30.4}/crates/polyxml-python/tests/test_xsi_type.py +0 -0
  68. {polyxml-0.30.3 → polyxml-0.30.4}/python/polyxml/AGENT_GUIDE.md +0 -0
  69. {polyxml-0.30.3 → polyxml-0.30.4}/python/polyxml/__init__.py +0 -0
  70. {polyxml-0.30.3 → polyxml-0.30.4}/python/polyxml/compat/__init__.py +0 -0
  71. {polyxml-0.30.3 → polyxml-0.30.4}/python/polyxml/compat/xsdata.py +0 -0
  72. {polyxml-0.30.3 → polyxml-0.30.4}/python/polyxml/py.typed +0 -0
@@ -608,7 +608,7 @@ dependencies = [
608
608
 
609
609
  [[package]]
610
610
  name = "polyxml"
611
- version = "0.30.3"
611
+ version = "0.30.4"
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.30.3"
632
+ version = "0.30.4"
633
633
  dependencies = [
634
634
  "polyxml",
635
635
  ]
636
636
 
637
637
  [[package]]
638
638
  name = "polyxml-cli"
639
- version = "0.30.3"
639
+ version = "0.30.4"
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.30.3"
653
+ version = "0.30.4"
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.30.3"
663
+ version = "0.30.4"
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.30.3"
673
+ version = "0.30.4"
674
674
  dependencies = [
675
675
  "polyxml",
676
676
  "quick-xml",
@@ -3,7 +3,7 @@ resolver = "2"
3
3
  members = ["crates/polyxml-core", "crates/polyxml-python"]
4
4
 
5
5
  [workspace.package]
6
- version = "0.30.3"
6
+ version = "0.30.4"
7
7
  edition = "2021"
8
8
  rust-version = "1.80"
9
9
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: polyxml
3
- Version: 0.30.3
3
+ Version: 0.30.4
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -379,8 +379,12 @@ impl GoCodegen {
379
379
  .values()
380
380
  .any(|def| matches!(def, TypeDef::Union(u) if u.is_lexical()))
381
381
  {
382
- imports.push("\"strconv\"");
383
- imports.push("\"strings\"");
382
+ if body.contains("strconv.") {
383
+ imports.push("\"strconv\"");
384
+ }
385
+ if body.contains("strings.") {
386
+ imports.push("\"strings\"");
387
+ }
384
388
  }
385
389
  if has_time {
386
390
  imports.push("\"time\"");
@@ -765,6 +769,11 @@ impl GoCodegen {
765
769
  for (branch, field) in u.branches.iter().zip(&fields) {
766
770
  let mapped = self.context.map_type_ref(&branch.type_ref);
767
771
  if let TypeRef::Named(qname) = &branch.type_ref {
772
+ if matches!(ir.types.get(qname), Some(TypeDef::Union(inner)) if inner.is_lexical())
773
+ {
774
+ let _ = writeln!(out, " {{ var escaped strings.Builder; if err := xml.EscapeText(&escaped, []byte(value)); err != nil {{ return err }}; var v {mapped}; if err := xml.Unmarshal([]byte(\"<v>\"+escaped.String()+\"</v>\"), &v); err == nil {{ c.{field} = &v; return nil }} }}");
775
+ continue;
776
+ }
768
777
  if let Some(TypeDef::Enum(def)) = ir.types.get(qname) {
769
778
  let comparisons = def
770
779
  .variants
@@ -861,6 +870,11 @@ impl GoCodegen {
861
870
  );
862
871
  out.push_str(" count := 0\n var value string\n");
863
872
  for (branch, field) in u.branches.iter().zip(&fields) {
873
+ if matches!(&branch.type_ref, TypeRef::Named(qname) if matches!(ir.types.get(qname), Some(TypeDef::Union(inner)) if inner.is_lexical()))
874
+ {
875
+ let _ = writeln!(out, " if c.{field} != nil {{ data, err := xml.Marshal(c.{field}); if err != nil {{ return err }}; if err := xml.Unmarshal(data, &value); err != nil {{ return err }}; count++ }}");
876
+ continue;
877
+ }
864
878
  let expr = if matches!(
865
879
  super::primitive_base(&branch.type_ref, ir),
866
880
  TypeRef::Primitive(PrimitiveType::Date)
@@ -1264,10 +1278,15 @@ impl GoCodegen {
1264
1278
  }
1265
1279
 
1266
1280
  writeln!(out, "// Root XML Element Type Aliases").unwrap();
1281
+ let mut declared: HashSet<String> = ir
1282
+ .types
1283
+ .values()
1284
+ .map(|def| type_ident(def.qname()))
1285
+ .collect();
1267
1286
  for elem in ir.elements.values() {
1268
1287
  let elem_alias = to_go_type_name(&elem.qname.local);
1269
1288
  let target_type = self.context.map_type_ref(&elem.type_ref);
1270
- if elem_alias != target_type {
1289
+ if elem_alias != target_type && declared.insert(elem_alias.clone()) {
1271
1290
  if let Some(ref doc) = elem.documentation {
1272
1291
  for line in doc.lines() {
1273
1292
  let trimmed = line.trim();
@@ -599,11 +599,30 @@ impl TypeScriptCodegen {
599
599
  self.emit_docstring(out, doc, "");
600
600
  }
601
601
 
602
- let mut extends_clause = String::new();
603
- if let Some(ref base) = s.base_type {
604
- let base_name = type_ident(base);
605
- extends_clause = format!(" extends {}", base_name);
602
+ let base_name = s.base_type.as_ref().map(type_ident);
603
+ let mut inherited_fields = HashSet::new();
604
+ let mut current_base = s.base_type.as_ref();
605
+ let mut visited_bases = HashSet::new();
606
+ while let Some(base_qname) = current_base {
607
+ if !visited_bases.insert(base_qname.clone()) {
608
+ break;
609
+ }
610
+ let Some(TypeDef::Struct(base)) = ir.types.get(base_qname) else {
611
+ break;
612
+ };
613
+ let mut seen = HashSet::new();
614
+ for field in &base.fields {
615
+ inherited_fields.insert(self.unique_field_name(&field.name, &mut seen));
616
+ }
617
+ current_base = base.base_type.as_ref();
606
618
  }
619
+ let mut own_seen = HashSet::new();
620
+ let overridden = s
621
+ .fields
622
+ .iter()
623
+ .map(|field| self.unique_field_name(&field.name, &mut own_seen))
624
+ .filter(|name| inherited_fields.contains(name))
625
+ .collect::<Vec<_>>();
607
626
 
608
627
  let readonly_prefix = if self.options.readonly_fields {
609
628
  "readonly "
@@ -611,13 +630,29 @@ impl TypeScriptCodegen {
611
630
  ""
612
631
  };
613
632
 
614
- if self.options.use_interface {
615
- let _ = writeln!(out, "export interface {}{} {{", ts_name, extends_clause);
616
- } else if extends_clause.is_empty() {
617
- let _ = writeln!(out, "export type {} = {{", ts_name);
633
+ if let Some(base_name) = &base_name {
634
+ if overridden.is_empty() {
635
+ if self.options.use_interface {
636
+ let _ = writeln!(out, "export interface {} extends {} {{", ts_name, base_name);
637
+ } else {
638
+ let _ = writeln!(out, "export type {} = {} & {{", ts_name, base_name);
639
+ }
640
+ } else {
641
+ let keys = overridden
642
+ .iter()
643
+ .map(|name| format!("{name:?}"))
644
+ .collect::<Vec<_>>()
645
+ .join(" | ");
646
+ let _ = writeln!(
647
+ out,
648
+ "export type {} = Omit<{}, {}> & {{",
649
+ ts_name, base_name, keys
650
+ );
651
+ }
652
+ } else if self.options.use_interface {
653
+ let _ = writeln!(out, "export interface {} {{", ts_name);
618
654
  } else {
619
- let base_name = type_ident(s.base_type.as_ref().unwrap());
620
- let _ = writeln!(out, "export type {} = {} & {{", ts_name, base_name);
655
+ let _ = writeln!(out, "export type {} = {{", ts_name);
621
656
  }
622
657
 
623
658
  let mut seen_fields = HashSet::new();
@@ -456,6 +456,9 @@ pub struct SchemaIR {
456
456
  pub namespaces: Vec<NamespaceDeclaration>,
457
457
  pub types: BTreeMap<QName, TypeDef>,
458
458
  pub elements: BTreeMap<QName, ElementDef>,
459
+ /// Declared types of global attributes, used to resolve `<xs:attribute ref>`.
460
+ #[serde(default)]
461
+ pub attributes: BTreeMap<QName, TypeRef>,
459
462
  pub substitution_groups: HashMap<QName, Vec<QName>>,
460
463
  /// Type owners outside the module currently being emitted. Definitions
461
464
  /// remain in `types` so generators can resolve their field semantics.
@@ -171,6 +171,7 @@ impl XsdParser {
171
171
  inherit_pattern_facets(&mut ir);
172
172
  self.expand_group_refs(&mut ir, self.frame_depth == 0);
173
173
  if self.frame_depth == 0 {
174
+ resolve_global_field_refs(&mut ir);
174
175
  compile_mixed_types(&mut ir);
175
176
  }
176
177
  ir.resolve_cycles();
@@ -304,6 +305,22 @@ impl XsdParser {
304
305
  ir.add_element(elem_def);
305
306
  }
306
307
  }
308
+ "attribute" => {
309
+ if let (Some(name), Some(ty)) =
310
+ (get_attr_value(e, "name"), get_attr_value(e, "type"))
311
+ {
312
+ ir.attributes
313
+ .entry(QName::new(target_namespace.as_deref(), name))
314
+ .or_insert_with(|| {
315
+ resolve_type_ref(
316
+ &ty,
317
+ target_namespace.as_deref(),
318
+ &prefixes,
319
+ )
320
+ });
321
+ }
322
+ skip_subtree(&mut reader)?;
323
+ }
307
324
  "group" => {
308
325
  // Named model group definition.
309
326
  if let Some(gname) = get_attr_value(e, "name") {
@@ -373,6 +390,21 @@ impl XsdParser {
373
390
  ir.add_element(elem_def);
374
391
  }
375
392
  }
393
+ "attribute" => {
394
+ if let (Some(name), Some(ty)) =
395
+ (get_attr_value(e, "name"), get_attr_value(e, "type"))
396
+ {
397
+ ir.attributes
398
+ .entry(QName::new(target_namespace.as_deref(), name))
399
+ .or_insert_with(|| {
400
+ resolve_type_ref(
401
+ &ty,
402
+ target_namespace.as_deref(),
403
+ &prefixes,
404
+ )
405
+ });
406
+ }
407
+ }
376
408
  "group" => {
377
409
  // Empty named model group (no particles).
378
410
  if let Some(gname) = get_attr_value(e, "name") {
@@ -1497,6 +1529,68 @@ fn parse_union_members(
1497
1529
  .collect()
1498
1530
  }
1499
1531
 
1532
+ /// Replace element-reference placeholders with their global element's declared type.
1533
+ /// Referenced elements may be defined in an included file parsed later.
1534
+ fn resolve_global_field_refs(ir: &mut SchemaIR) {
1535
+ let elements = &ir.elements;
1536
+ let attributes = &ir.attributes;
1537
+ let type_names: HashSet<QName> = ir.types.keys().cloned().collect();
1538
+ fn resolve(
1539
+ reference: &mut TypeRef,
1540
+ elements: &BTreeMap<QName, ElementDef>,
1541
+ types: &HashSet<QName>,
1542
+ ) {
1543
+ let TypeRef::Named(qname) = reference else {
1544
+ return;
1545
+ };
1546
+ if types.contains(qname) {
1547
+ return;
1548
+ }
1549
+ let mut current = qname.clone();
1550
+ let mut seen = HashSet::new();
1551
+ while seen.insert(current.clone()) {
1552
+ let Some(element) = elements.get(&current) else {
1553
+ break;
1554
+ };
1555
+ if let TypeRef::Named(next) = &element.type_ref {
1556
+ if !types.contains(next) && elements.contains_key(next) {
1557
+ current = next.clone();
1558
+ continue;
1559
+ }
1560
+ }
1561
+ *reference = element.type_ref.clone();
1562
+ break;
1563
+ }
1564
+ }
1565
+ for def in ir.types.values_mut() {
1566
+ match def {
1567
+ TypeDef::Struct(structure) => {
1568
+ for field in &mut structure.fields {
1569
+ match field.kind {
1570
+ FieldKind::Element => resolve(&mut field.type_ref, elements, &type_names),
1571
+ FieldKind::Attribute => {
1572
+ if let TypeRef::Named(qname) = &field.type_ref {
1573
+ if !type_names.contains(qname) {
1574
+ if let Some(type_ref) = attributes.get(qname) {
1575
+ field.type_ref = type_ref.clone();
1576
+ }
1577
+ }
1578
+ }
1579
+ }
1580
+ _ => {}
1581
+ }
1582
+ }
1583
+ }
1584
+ TypeDef::Union(union) if !union.is_lexical() => {
1585
+ for branch in &mut union.branches {
1586
+ resolve(&mut branch.type_ref, elements, &type_names);
1587
+ }
1588
+ }
1589
+ _ => {}
1590
+ }
1591
+ }
1592
+ }
1593
+
1500
1594
  /// Give XSD Gregorian primitives named, validated types in every target.
1501
1595
  /// The original primitive remains the base of each synthesized simple type.
1502
1596
  fn add_gregorian_types(ir: &mut SchemaIR) {
@@ -1540,6 +1634,9 @@ fn add_gregorian_types(ir: &mut SchemaIR) {
1540
1634
  rewrite(&mut element.type_ref, &mut used);
1541
1635
  }
1542
1636
  for def in ir.types.values_mut() {
1637
+ if def.qname().namespace.as_deref() == Some(NS) {
1638
+ continue;
1639
+ }
1543
1640
  match def {
1544
1641
  TypeDef::Struct(s) => {
1545
1642
  for field in &mut s.fields {
@@ -1884,6 +1981,15 @@ fn rekey_to_namespace(ir: &mut SchemaIR, ns: &str) {
1884
1981
  }
1885
1982
  ir.elements = elements;
1886
1983
 
1984
+ let old_attributes = std::mem::take(&mut ir.attributes);
1985
+ for (mut qname, mut type_ref) in old_attributes {
1986
+ if qname.namespace.is_none() {
1987
+ qname = QName::new(Some(ns.to_string()), qname.local);
1988
+ }
1989
+ rekey_type_ref(&mut type_ref, ns);
1990
+ ir.attributes.insert(qname, type_ref);
1991
+ }
1992
+
1887
1993
  let old_subs = std::mem::take(&mut ir.substitution_groups);
1888
1994
  let mut subs = HashMap::new();
1889
1995
  for (k, v) in old_subs {
@@ -2040,6 +2146,9 @@ fn merge_ir(dest: &mut SchemaIR, src: SchemaIR) {
2040
2146
  for (k, v) in src.elements {
2041
2147
  dest.elements.insert(k, v);
2042
2148
  }
2149
+ for (k, v) in src.attributes {
2150
+ dest.attributes.insert(k, v);
2151
+ }
2043
2152
  for (k, v) in src.substitution_groups {
2044
2153
  dest.substitution_groups.entry(k).or_default().extend(v);
2045
2154
  }
@@ -0,0 +1,17 @@
1
+ <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
2
+ targetNamespace="urn:polyxml:ts-mixed" xmlns:m="urn:polyxml:ts-mixed"
3
+ elementFormDefault="qualified">
4
+ <xs:complexType name="Bin" mixed="true" abstract="true">
5
+ <xs:sequence/>
6
+ </xs:complexType>
7
+ <xs:complexType name="Ed" mixed="true">
8
+ <xs:complexContent mixed="true">
9
+ <xs:extension base="m:Bin">
10
+ <xs:sequence>
11
+ <xs:element name="reference" type="xs:string" minOccurs="0"/>
12
+ <xs:element name="thumbnail" type="xs:string" minOccurs="0"/>
13
+ </xs:sequence>
14
+ </xs:extension>
15
+ </xs:complexContent>
16
+ </xs:complexType>
17
+ </xs:schema>
@@ -10,6 +10,99 @@ use polyxml::ir::{
10
10
  Cardinality, EnumDef, EnumValue, FieldDef, FieldKind, PrimitiveType, QName, RestrictionFacets,
11
11
  SchemaIR, StructDef, TypeDef, TypeRef, UnionBranch, UnionDef,
12
12
  };
13
+ use polyxml::schema_parser::XsdParser;
14
+
15
+ #[test]
16
+ fn referenced_element_uses_declared_type_after_includes() {
17
+ let dir = tempdir().unwrap();
18
+ fs::write(
19
+ dir.path().join("base.xsd"),
20
+ r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:test" xmlns:t="urn:test">
21
+ <xs:complexType name="Base"><xs:sequence><xs:element ref="t:description"/></xs:sequence></xs:complexType>
22
+ <xs:element name="description" type="xs:string"/>
23
+ <xs:element name="day" type="xs:gDay"/>
24
+ </xs:schema>"#,
25
+ )
26
+ .unwrap();
27
+ fs::write(
28
+ dir.path().join("main.xsd"),
29
+ r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:test" xmlns:t="urn:test">
30
+ <xs:include schemaLocation="base.xsd"/>
31
+ <xs:complexType name="Derived"><xs:complexContent><xs:extension base="t:Base"/></xs:complexContent></xs:complexType>
32
+ </xs:schema>"#,
33
+ )
34
+ .unwrap();
35
+ let ir = XsdParser::new()
36
+ .parse_file(dir.path().join("main.xsd"))
37
+ .unwrap();
38
+ let base = match ir.types.get(&QName::new(Some("urn:test"), "Base")).unwrap() {
39
+ TypeDef::Struct(value) => value,
40
+ _ => panic!("expected struct"),
41
+ };
42
+ assert_eq!(
43
+ base.fields[0].type_ref,
44
+ TypeRef::Primitive(PrimitiveType::String)
45
+ );
46
+ let go = GoCodegen::new(GoOptions::default()).generate_module(&ir);
47
+ assert!(go.contains("type GDay string"));
48
+ assert!(!go.contains("type GDay GDay"));
49
+ }
50
+
51
+ #[test]
52
+ fn nested_lexical_unions_compile_and_round_trip_in_go() {
53
+ if Command::new("go").arg("version").output().is_err() {
54
+ return;
55
+ }
56
+ let schema = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:test">
57
+ <xs:simpleType name="Inner"><xs:union memberTypes="xs:int xs:string"/></xs:simpleType>
58
+ <xs:simpleType name="Outer"><xs:union memberTypes="Inner xs:boolean"/></xs:simpleType>
59
+ </xs:schema>"#;
60
+ let ir = XsdParser::new().parse_str(schema).unwrap();
61
+ let dir = tempdir().unwrap();
62
+ fs::write(dir.path().join("go.mod"), "module nested\n\ngo 1.22\n").unwrap();
63
+ fs::write(
64
+ dir.path().join("models.go"),
65
+ GoCodegen::new(GoOptions::default()).generate_module(&ir),
66
+ )
67
+ .unwrap();
68
+ fs::write(
69
+ dir.path().join("models_test.go"),
70
+ r#"package models
71
+ import ("encoding/xml"; "testing")
72
+ func TestNestedUnion(t *testing.T) {
73
+ var value Outer
74
+ if err := xml.Unmarshal([]byte("<value>42</value>"), &value); err != nil { t.Fatal(err) }
75
+ if value.InnerValue == nil || value.InnerValue.IntValue == nil { t.Fatalf("wrong branch: %+v", value) }
76
+ data, err := xml.Marshal(value)
77
+ if err != nil { t.Fatal(err) }
78
+ if string(data) != "<Outer>42</Outer>" { t.Fatalf("wrong XML: %s", data) }
79
+ }"#,
80
+ )
81
+ .unwrap();
82
+ let result = Command::new("go")
83
+ .args(["test", "./..."])
84
+ .current_dir(dir.path())
85
+ .output()
86
+ .unwrap();
87
+ assert!(
88
+ result.status.success(),
89
+ "{}",
90
+ String::from_utf8_lossy(&result.stderr)
91
+ );
92
+ }
93
+
94
+ #[test]
95
+ fn root_alias_names_are_unique_after_go_normalization() {
96
+ let schema = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:test">
97
+ <xs:complexType name="FirstType"/>
98
+ <xs:complexType name="SecondType"/>
99
+ <xs:element name="item" type="FirstType"/>
100
+ <xs:element name="item_" type="SecondType"/>
101
+ </xs:schema>"#;
102
+ let ir = XsdParser::new().parse_str(schema).unwrap();
103
+ let code = GoCodegen::new(GoOptions::default()).generate_module(&ir);
104
+ assert_eq!(code.matches("type Item = ").count(), 1);
105
+ }
13
106
 
14
107
  #[test]
15
108
  fn test_go_sanitization() {
@@ -27,6 +27,47 @@ fn consumed_child_subtrees_do_not_swallow_following_types() {
27
27
  .contains_key(&QName::new(Some("urn:test"), "Second")));
28
28
  }
29
29
 
30
+ #[test]
31
+ fn consumed_particles_do_not_swallow_following_declarations() {
32
+ let xsd = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:test" xmlns:t="urn:test">
33
+ <xs:group name="Shared"><xs:sequence>
34
+ <xs:element name="inside"><xs:annotation><xs:documentation>text</xs:documentation></xs:annotation></xs:element>
35
+ </xs:sequence></xs:group>
36
+ <xs:complexType name="First"><xs:sequence>
37
+ <xs:group ref="t:Shared"><xs:annotation/></xs:group>
38
+ <xs:element name="child" type="xs:string"><xs:annotation/></xs:element>
39
+ </xs:sequence></xs:complexType>
40
+ <xs:complexType name="Second"><xs:sequence><xs:element name="value" type="xs:string"/></xs:sequence></xs:complexType>
41
+ </xs:schema>"#;
42
+ let ir = XsdParser::new().parse_str(xsd).unwrap();
43
+ let first = match &ir.types[&QName::new(Some("urn:test"), "First")] {
44
+ TypeDef::Struct(value) => value,
45
+ _ => panic!("expected struct"),
46
+ };
47
+ assert!(first.fields.iter().any(|field| field.name == "inside"));
48
+ assert!(first.fields.iter().any(|field| field.name == "child"));
49
+ assert!(ir
50
+ .types
51
+ .contains_key(&QName::new(Some("urn:test"), "Second")));
52
+ }
53
+
54
+ #[test]
55
+ fn global_attribute_reference_uses_declared_type() {
56
+ let xsd = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:test" xmlns:t="urn:test">
57
+ <xs:complexType name="Object"><xs:attribute ref="t:id" use="required"/></xs:complexType>
58
+ <xs:attribute name="id" type="xs:ID"><xs:annotation><xs:documentation>identifier</xs:documentation></xs:annotation></xs:attribute>
59
+ </xs:schema>"#;
60
+ let ir = XsdParser::new().parse_str(xsd).unwrap();
61
+ let object = match &ir.types[&QName::new(Some("urn:test"), "Object")] {
62
+ TypeDef::Struct(value) => value,
63
+ _ => panic!("expected struct"),
64
+ };
65
+ assert_eq!(
66
+ object.fields[0].type_ref,
67
+ TypeRef::Primitive(PrimitiveType::Id)
68
+ );
69
+ }
70
+
30
71
  #[test]
31
72
  fn test_parse_lexical_union_member_types_and_inline_members() {
32
73
  let xsd = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:t="urn:test" targetNamespace="urn:test">
@@ -6,6 +6,35 @@ use polyxml::ir::{
6
6
  Cardinality, EnumDef, EnumValue, FieldDef, FieldKind, PrimitiveType, QName, RestrictionFacets,
7
7
  SchemaIR, SimpleTypeDef, StructDef, TypeDef, TypeRef, UnionBranch, UnionDef,
8
8
  };
9
+ use polyxml::schema_parser::XsdParser;
10
+ use std::{fs, process::Command};
11
+ use tempfile::tempdir;
12
+
13
+ #[test]
14
+ fn mixed_content_inheritance_compiles_with_widened_items() {
15
+ let schema = concat!(
16
+ env!("CARGO_MANIFEST_DIR"),
17
+ "/tests/fixtures/ts_mixed_content_inheritance.xsd"
18
+ );
19
+ let ir = XsdParser::new().parse_file(schema).unwrap();
20
+ let code = TypeScriptCodegen::new(TypeScriptOptions::default()).generate_module(&ir);
21
+ assert!(code.contains("export type Ed = Omit<Bin, \"items\"> & {"));
22
+ if Command::new("tsc").arg("--version").output().is_ok() {
23
+ let dir = tempdir().unwrap();
24
+ let source = dir.path().join("models.ts");
25
+ fs::write(&source, code).unwrap();
26
+ let result = Command::new("tsc")
27
+ .args(["--noEmit", "--skipLibCheck", "--target", "ES2020"])
28
+ .arg(&source)
29
+ .output()
30
+ .unwrap();
31
+ assert!(
32
+ result.status.success(),
33
+ "{}",
34
+ String::from_utf8_lossy(&result.stdout)
35
+ );
36
+ }
37
+ }
9
38
 
10
39
  #[test]
11
40
  fn test_ts_identifier_sanitization() {
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "polyxml"
7
- version = "0.30.3"
7
+ version = "0.30.4"
8
8
  description = "High-performance, polyglot XML data-binding engine built in Rust"
9
9
  authors = [
10
10
  { name = "Bailey Nguyen", email = "bailey.tan.nguyen@gmail.com" }
File without changes