polyxml 0.34.6__tar.gz → 0.34.8__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 (94) hide show
  1. {polyxml-0.34.6 → polyxml-0.34.8}/Cargo.lock +6 -6
  2. {polyxml-0.34.6 → polyxml-0.34.8}/Cargo.toml +1 -1
  3. {polyxml-0.34.6 → polyxml-0.34.8}/PKG-INFO +1 -1
  4. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/src/codegen/cpp/mod.rs +20 -7
  5. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/src/codegen/csharp/mod.rs +19 -7
  6. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/src/codegen/go/mod.rs +6 -4
  7. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/src/codegen/java/mod.rs +5 -2
  8. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/src/codegen/mod.rs +34 -0
  9. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/src/codegen/python/mod.rs +9 -9
  10. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/src/codegen/rust/mod.rs +6 -6
  11. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/src/codegen/typescript/mod.rs +14 -4
  12. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/src/converters.rs +2 -8
  13. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/src/lib.rs +1 -0
  14. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/src/parser.rs +97 -65
  15. polyxml-0.34.8/crates/polyxml-core/src/pattern.rs +155 -0
  16. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/src/schema.rs +9 -0
  17. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/src/schema_parser/mod.rs +227 -137
  18. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/src/serializer.rs +228 -103
  19. polyxml-0.34.8/crates/polyxml-core/tests/test_default_literal_codegen.rs +150 -0
  20. polyxml-0.34.8/crates/polyxml-core/tests/test_enum_literal_codegen.rs +233 -0
  21. polyxml-0.34.8/crates/polyxml-core/tests/test_inherited_simple_content.rs +136 -0
  22. polyxml-0.34.8/crates/polyxml-core/tests/test_inherited_text_codegen.rs +226 -0
  23. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_java_codegen.rs +2 -1
  24. polyxml-0.34.8/crates/polyxml-core/tests/test_mixed_branch_lookup.rs +122 -0
  25. polyxml-0.34.8/crates/polyxml-core/tests/test_mixed_nil.rs +93 -0
  26. polyxml-0.34.8/crates/polyxml-core/tests/test_namespace_entities.rs +90 -0
  27. polyxml-0.34.8/crates/polyxml-core/tests/test_runtime_fast_paths.rs +246 -0
  28. polyxml-0.34.8/crates/polyxml-core/tests/test_schema_attribute_entities.rs +114 -0
  29. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-python/tests/test_generated_models.py +57 -7
  30. {polyxml-0.34.6 → polyxml-0.34.8}/pyproject.toml +1 -1
  31. {polyxml-0.34.6 → polyxml-0.34.8}/README.md +0 -0
  32. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/Cargo.toml +0 -0
  33. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/README.md +0 -0
  34. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/benches/core_benchmarks.rs +0 -0
  35. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/benches/support/tag_dispatch_fixtures.rs +0 -0
  36. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/benches/tag_dispatch.rs +0 -0
  37. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/src/codegen/java/codec.rs +0 -0
  38. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/src/codegen/java/models.rs +0 -0
  39. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/src/codegen/python/aot.rs +0 -0
  40. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/src/error.rs +0 -0
  41. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/src/integer.rs +0 -0
  42. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/src/ir/chunker.rs +0 -0
  43. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/src/ir/mod.rs +0 -0
  44. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/src/ir/particle.rs +0 -0
  45. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/src/ir/tarjan.rs +0 -0
  46. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/src/json.rs +0 -0
  47. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/src/schema_parser/upa.rs +0 -0
  48. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/src/transcoder.rs +0 -0
  49. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/src/value.rs +0 -0
  50. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/fixtures/lexical_union.xsd +0 -0
  51. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/fixtures/mixed_content.xsd +0 -0
  52. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/fixtures/ts_mixed_content_inheritance.xsd +0 -0
  53. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_adversarial_identifiers.rs +0 -0
  54. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_any_attribute.rs +0 -0
  55. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_any_element.rs +0 -0
  56. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_conformance.rs +0 -0
  57. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_core.rs +0 -0
  58. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_cpp_codegen.rs +0 -0
  59. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_csharp_codegen.rs +0 -0
  60. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_go_codegen.rs +0 -0
  61. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_json.rs +0 -0
  62. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_lexical_lists.rs +0 -0
  63. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_lexical_union_codegen.rs +0 -0
  64. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_mixed_content.rs +0 -0
  65. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_pattern_codegen.rs +0 -0
  66. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_python_codegen.rs +0 -0
  67. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_qualified_attributes.rs +0 -0
  68. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_rust_codecs.rs +0 -0
  69. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_rust_codegen.rs +0 -0
  70. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_schema_audit.rs +0 -0
  71. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_schema_ir.rs +0 -0
  72. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_security.rs +0 -0
  73. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_temporal_union.rs +0 -0
  74. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_transcoder.rs +0 -0
  75. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_ts_codegen.rs +0 -0
  76. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_ubl_csharp.rs +0 -0
  77. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_unbounded_choice.rs +0 -0
  78. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_upa.rs +0 -0
  79. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_value_constraints.rs +0 -0
  80. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-core/tests/test_xsi_type.rs +0 -0
  81. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-python/Cargo.toml +0 -0
  82. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-python/README.md +0 -0
  83. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-python/benches/aot_vs_dataclass.py +0 -0
  84. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-python/src/lib.rs +0 -0
  85. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-python/tests/test_conformance.py +0 -0
  86. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-python/tests/test_json.py +0 -0
  87. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-python/tests/test_mixed_content.py +0 -0
  88. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-python/tests/test_polyxml.py +0 -0
  89. {polyxml-0.34.6 → polyxml-0.34.8}/crates/polyxml-python/tests/test_xsi_type.py +0 -0
  90. {polyxml-0.34.6 → polyxml-0.34.8}/python/polyxml/AGENT_GUIDE.md +0 -0
  91. {polyxml-0.34.6 → polyxml-0.34.8}/python/polyxml/__init__.py +0 -0
  92. {polyxml-0.34.6 → polyxml-0.34.8}/python/polyxml/compat/__init__.py +0 -0
  93. {polyxml-0.34.6 → polyxml-0.34.8}/python/polyxml/compat/xsdata.py +0 -0
  94. {polyxml-0.34.6 → polyxml-0.34.8}/python/polyxml/py.typed +0 -0
@@ -608,7 +608,7 @@ dependencies = [
608
608
 
609
609
  [[package]]
610
610
  name = "polyxml"
611
- version = "0.34.6"
611
+ version = "0.34.8"
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.34.6"
632
+ version = "0.34.8"
633
633
  dependencies = [
634
634
  "polyxml",
635
635
  ]
636
636
 
637
637
  [[package]]
638
638
  name = "polyxml-cli"
639
- version = "0.34.6"
639
+ version = "0.34.8"
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.34.6"
653
+ version = "0.34.8"
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.34.6"
663
+ version = "0.34.8"
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.34.6"
673
+ version = "0.34.8"
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.34.6"
6
+ version = "0.34.8"
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.34.6
3
+ Version: 0.34.8
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -553,7 +553,14 @@ endif()
553
553
  } else {
554
554
  ""
555
555
  };
556
- writeln!(out, " {:?}, T::{}{}", v.value, var_name, comma).unwrap();
556
+ writeln!(
557
+ out,
558
+ " {}, T::{}{}",
559
+ super::cpp_string_literal(&v.value),
560
+ var_name,
561
+ comma
562
+ )
563
+ .unwrap();
557
564
  }
558
565
  writeln!(out, " );").unwrap();
559
566
  }
@@ -790,8 +797,10 @@ inline int polyxml_integer_compare(std::string_view a,std::string_view b) noexce
790
797
  for (variant, var_name) in enum_def.variants.iter().zip(&variant_names) {
791
798
  writeln!(
792
799
  out,
793
- " case {}::{}: return \"{}\";",
794
- enum_name, var_name, variant.value
800
+ " case {}::{}: return {};",
801
+ enum_name,
802
+ var_name,
803
+ super::cpp_string_literal(&variant.value)
795
804
  )
796
805
  .unwrap();
797
806
  }
@@ -818,8 +827,10 @@ inline int polyxml_integer_compare(std::string_view a,std::string_view b) noexce
818
827
  for (variant, var_name) in enum_def.variants.iter().zip(&variant_names) {
819
828
  writeln!(
820
829
  out,
821
- " if (s == \"{}\") return {}::{};",
822
- variant.value, enum_name, var_name
830
+ " if (s == {}) return {}::{};",
831
+ super::cpp_string_literal(&variant.value),
832
+ enum_name,
833
+ var_name
823
834
  )
824
835
  .unwrap();
825
836
  }
@@ -998,7 +1009,9 @@ inline int polyxml_integer_compare(std::string_view a,std::string_view b) noexce
998
1009
  // Value field
999
1010
  let init = match &f.default_value {
1000
1011
  Some(v) => match f.type_ref {
1001
- TypeRef::Primitive(p) if p.is_unbounded_integer() => format!(" = {v:?}"),
1012
+ TypeRef::Primitive(p) if p.is_unbounded_integer() => {
1013
+ format!(" = {}", super::cpp_string_literal(v))
1014
+ }
1002
1015
  TypeRef::Primitive(PrimitiveType::Boolean) => {
1003
1016
  format!(" = {}", v.to_lowercase())
1004
1017
  }
@@ -1008,7 +1021,7 @@ inline int polyxml_integer_compare(std::string_view a,std::string_view b) noexce
1008
1021
  TypeRef::Primitive(PrimitiveType::String)
1009
1022
  | TypeRef::Primitive(PrimitiveType::Token)
1010
1023
  | TypeRef::Primitive(PrimitiveType::NormalizedString) => {
1011
- format!(" = \"{}\"", v.replace('\\', "\\\\").replace('"', "\\\""))
1024
+ format!(" = {}", super::cpp_string_literal(v))
1012
1025
  }
1013
1026
  TypeRef::Primitive(_) => format!(" = {}", v),
1014
1027
  TypeRef::Named(_) => format!(" = {}", v),
@@ -618,7 +618,13 @@ impl CSharpCodegen {
618
618
  self.emit_docstring(out, doc, &format!("{} ", indent));
619
619
  }
620
620
  if self.options.emit_xml_attributes {
621
- writeln!(out, "{} [XmlEnum(\"{}\")]", indent, variant.value).unwrap();
621
+ writeln!(
622
+ out,
623
+ "{} [XmlEnum({})]",
624
+ indent,
625
+ super::string_literal(&variant.value)
626
+ )
627
+ .unwrap();
622
628
  }
623
629
  writeln!(out, "{} {},", indent, variant_name).unwrap();
624
630
  }
@@ -659,8 +665,11 @@ impl CSharpCodegen {
659
665
  for (variant, variant_name) in e.variants.iter().zip(&variant_names) {
660
666
  writeln!(
661
667
  out,
662
- "{} {}.{} => \"{}\",",
663
- indent, enum_name, variant_name, variant.value
668
+ "{} {}.{} => {},",
669
+ indent,
670
+ enum_name,
671
+ variant_name,
672
+ super::string_literal(&variant.value)
664
673
  )
665
674
  .unwrap();
666
675
  }
@@ -1295,10 +1304,10 @@ impl CSharpCodegen {
1295
1304
  if let Some(kind) = integer_kind {
1296
1305
  return format!("PolyxmlIntegerLexical.Validate({text},{kind})");
1297
1306
  }
1298
- format!("({base})new XmlSerializer(typeof({base}), new XmlRootAttribute(\"value\")).Deserialize(new System.IO.StringReader(\"<value>\" + System.Security.SecurityElement.Escape({text}) + \"</value>\"))!")
1307
+ format!("({base})new XmlSerializer(typeof({base}), new XmlRootAttribute(\"value\")).Deserialize(new System.IO.StringReader(\"<value>\" + System.Security.SecurityElement.Escape({text}).Replace(\"\\r\", \"&#13;\") + \"</value>\"))!")
1299
1308
  };
1300
1309
  if let Some(fixed) = &field.fixed_value {
1301
- let expected = parse(&format!("{fixed:?}"));
1310
+ let expected = parse(&super::string_literal(fixed));
1302
1311
  let initial = if field.cardinality.is_optional() && field.kind == FieldKind::Element {
1303
1312
  "default!".to_string()
1304
1313
  } else {
@@ -1318,7 +1327,7 @@ impl CSharpCodegen {
1318
1327
  {indent} [XmlIgnore] public {ty} {name} {{ get => _{name}; set {{ if ({present}!({equal})) throw new System.ComponentModel.DataAnnotations.ValidationException(\"Fixed value constraint violated for {name}\"); _{name} = value; }} }}").unwrap();
1319
1328
  } else {
1320
1329
  let initial = if field.kind == FieldKind::Attribute {
1321
- parse(&format!("{default:?}"))
1330
+ parse(&super::string_literal(default))
1322
1331
  } else {
1323
1332
  "default!".to_string()
1324
1333
  };
@@ -1352,7 +1361,10 @@ impl CSharpCodegen {
1352
1361
  let text = if field.kind == FieldKind::Attribute {
1353
1362
  "value".into()
1354
1363
  } else {
1355
- format!("value.Length == 0 ? {default:?} : value")
1364
+ format!(
1365
+ "value.Length == 0 ? {} : value",
1366
+ super::string_literal(default)
1367
+ )
1356
1368
  };
1357
1369
  let parsed = parse("text");
1358
1370
  writeln!(out, "{indent} set {{ if (value is null) {{ {name} = default!; return; }} var text = {text}; {name} = {parsed}; }}
@@ -577,7 +577,7 @@ func (v {type_name}) MarshalText() ([]byte,error) {{ tokens:=make([]string,0,len
577
577
  let variants = enumeration
578
578
  .variants
579
579
  .iter()
580
- .map(|v| format!("{:?}", v.value))
580
+ .map(|v| super::string_literal(&v.value))
581
581
  .collect::<Vec<_>>()
582
582
  .join(",");
583
583
  writeln!(out,"switch string(value) {{case {variants}: default:return fmt.Errorf(\"invalid enum list item\")}}").unwrap();
@@ -670,8 +670,10 @@ func (v {type_name}) MarshalText() ([]byte,error) {{ tokens:=make([]string,0,len
670
670
  }
671
671
  writeln!(
672
672
  out,
673
- " {} {} = \"{}\"",
674
- const_name, enum_name, variant.value
673
+ " {} {} = {}",
674
+ const_name,
675
+ enum_name,
676
+ super::string_literal(&variant.value)
675
677
  )
676
678
  .unwrap();
677
679
  }
@@ -925,7 +927,7 @@ func (v {type_name}) MarshalText() ([]byte,error) {{ tokens:=make([]string,0,len
925
927
  let comparisons = def
926
928
  .variants
927
929
  .iter()
928
- .map(|v| format!("value == {:?}", v.value))
930
+ .map(|v| format!("value == {}", super::string_literal(&v.value)))
929
931
  .collect::<Vec<_>>()
930
932
  .join(" || ");
931
933
  let _ = writeln!(
@@ -597,8 +597,11 @@ impl JavaCodegen {
597
597
  let semi_or_comma = if i + 1 == e.variants.len() { ";" } else { "," };
598
598
  let _ = writeln!(
599
599
  out,
600
- "{} {}({:?}){}",
601
- indent, const_name, v.value, semi_or_comma
600
+ "{} {}({}){}",
601
+ indent,
602
+ const_name,
603
+ super::string_literal(&v.value),
604
+ semi_or_comma
602
605
  );
603
606
  }
604
607
 
@@ -24,6 +24,40 @@ pub use python::{
24
24
  pub use rust::{RustCodegen, RustOptions};
25
25
  pub use typescript::{TypeScriptBackend, TypeScriptCodegen, TypeScriptOptions};
26
26
 
27
+ /// JSON string escapes are also valid in Python, Go, Java, C# and TypeScript
28
+ /// string literals. Rust and C++ need their own escapes for control characters.
29
+ pub(crate) fn string_literal(value: &str) -> String {
30
+ serde_json::to_string(value)
31
+ .expect("serializing a string cannot fail")
32
+ // C# treats these Unicode characters as source line terminators even
33
+ // inside quoted strings; JSON permits their literal forms.
34
+ .replace('\u{85}', "\\u0085")
35
+ .replace('\u{2028}', "\\u2028")
36
+ .replace('\u{2029}', "\\u2029")
37
+ }
38
+
39
+ pub(crate) fn cpp_string_literal(value: &str) -> String {
40
+ use std::fmt::Write;
41
+ let mut literal = String::from("\"");
42
+ for character in value.chars() {
43
+ match character {
44
+ '\\' => literal.push_str("\\\\"),
45
+ '"' => literal.push_str("\\\""),
46
+ '\n' => literal.push_str("\\n"),
47
+ '\r' => literal.push_str("\\r"),
48
+ '\t' => literal.push_str("\\t"),
49
+ c if c.is_ascii_control() => {
50
+ // Three octal digits prevent a following digit from extending
51
+ // the escape; universal character names cannot encode these.
52
+ write!(literal, "\\{:03o}", c as u32).unwrap();
53
+ }
54
+ c => literal.push(c),
55
+ }
56
+ }
57
+ literal.push('"');
58
+ literal
59
+ }
60
+
27
61
  /// Prefix every physical XSD documentation line so embedded newlines cannot
28
62
  /// escape a generated source comment.
29
63
  pub(crate) fn write_documentation_lines(out: &mut String, prefix: &str, doc: &str) {
@@ -521,9 +521,9 @@ impl PythonCodegen {
521
521
 
522
522
  let _ = writeln!(
523
523
  out,
524
- " {} = \"{}\"",
524
+ " {} = {}",
525
525
  var_id,
526
- variant.value.replace('"', "\\\"")
526
+ super::string_literal(&variant.value)
527
527
  );
528
528
  }
529
529
  }
@@ -1012,10 +1012,10 @@ impl PythonCodegen {
1012
1012
  }
1013
1013
 
1014
1014
  if let Some(ref fixed) = field.fixed_value {
1015
- parts.push(format!("\"fixed\": {fixed:?}"));
1015
+ parts.push(format!("\"fixed\": {}", super::string_literal(fixed)));
1016
1016
  }
1017
1017
  if let Some(ref default) = field.default_value {
1018
- parts.push(format!("\"default\": {default:?}"));
1018
+ parts.push(format!("\"default\": {}", super::string_literal(default)));
1019
1019
  }
1020
1020
  if matches!(super::primitive_base(&field.type_ref, ir), TypeRef::List(_)) {
1021
1021
  parts.push("\"tokens\": True".into());
@@ -1045,7 +1045,7 @@ impl PythonCodegen {
1045
1045
  fn format_default_value(&self, val: &str, type_ref: &TypeRef, ir: &SchemaIR) -> String {
1046
1046
  if matches!(super::primitive_base(type_ref, ir), TypeRef::Primitive(p) if p.is_unbounded_integer())
1047
1047
  {
1048
- return format!("int({val:?})");
1048
+ return format!("int({})", super::string_literal(val));
1049
1049
  }
1050
1050
 
1051
1051
  let base = super::primitive_base(type_ref, ir);
@@ -1073,20 +1073,20 @@ impl PythonCodegen {
1073
1073
  if val.trim().parse::<i64>().is_ok() {
1074
1074
  val.trim().to_string()
1075
1075
  } else {
1076
- format!("\"{}\"", val.replace('"', "\\\""))
1076
+ super::string_literal(val)
1077
1077
  }
1078
1078
  }
1079
1079
  TypeRef::Primitive(PrimitiveType::Float | PrimitiveType::Double) => {
1080
1080
  if val.trim().parse::<f64>().is_ok() {
1081
1081
  val.trim().to_string()
1082
1082
  } else {
1083
- format!("\"{}\"", val.replace('"', "\\\""))
1083
+ super::string_literal(val)
1084
1084
  }
1085
1085
  }
1086
1086
  TypeRef::Primitive(PrimitiveType::Decimal) => {
1087
- format!("Decimal(\"{}\")", val.replace('"', "\\\""))
1087
+ format!("Decimal({})", super::string_literal(val))
1088
1088
  }
1089
- _ => format!("\"{}\"", val.replace('"', "\\\"")),
1089
+ _ => super::string_literal(val),
1090
1090
  }
1091
1091
  }
1092
1092
 
@@ -724,7 +724,7 @@ impl RustCodegen {
724
724
  write_documentation_lines(out, " /// ", doc);
725
725
  }
726
726
  if self.options.derive_serde {
727
- let _ = writeln!(out, " #[serde(rename = \"{}\")]", variant.value);
727
+ let _ = writeln!(out, " #[serde(rename = {:?})]", variant.value);
728
728
  }
729
729
  if self.options.derive_default && idx == 0 {
730
730
  let _ = writeln!(out, " #[default]");
@@ -740,7 +740,7 @@ impl RustCodegen {
740
740
  out.push_str(" pub fn as_str(&self) -> &'static str {\n");
741
741
  out.push_str(" match self {\n");
742
742
  for (var_id, val) in &variant_map {
743
- let _ = writeln!(out, " Self::{} => \"{}\",", var_id, val);
743
+ let _ = writeln!(out, " Self::{} => {:?},", var_id, val);
744
744
  }
745
745
  if variant_map.is_empty() {
746
746
  out.push_str(" _ => \"\",\n");
@@ -755,7 +755,7 @@ impl RustCodegen {
755
755
  out.push_str(" fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {\n");
756
756
  out.push_str(" match s {\n");
757
757
  for (var_id, val) in &variant_map {
758
- let _ = writeln!(out, " \"{}\" => Ok(Self::{}),", val, var_id);
758
+ let _ = writeln!(out, " {:?} => Ok(Self::{}),", val, var_id);
759
759
  }
760
760
  let _ = writeln!(
761
761
  out,
@@ -844,7 +844,7 @@ impl RustCodegen {
844
844
  write_documentation_lines(out, " /// ", doc);
845
845
  }
846
846
  if self.options.derive_serde && !u.is_lexical() {
847
- let _ = writeln!(out, " #[serde(rename = \"{}\")]", branch.xml_name);
847
+ let _ = writeln!(out, " #[serde(rename = {:?})]", branch.xml_name);
848
848
  }
849
849
  if self.options.emit_polyxml_attrs && !u.is_lexical() {
850
850
  let _ = writeln!(out, " #[polyxml(element = \"{}\")]", branch.xml_name);
@@ -2218,8 +2218,8 @@ impl RustCodegen {
2218
2218
  /// Decode the simpleContent value field from the element's own text
2219
2219
  /// content. Emitted only when the struct has no child elements (the
2220
2220
  /// element loop already consumes through `End(start)`), and only for
2221
- /// scalar-backed text: a simpleContent chain whose value is typed as its
2222
- /// base struct has no scalar form here and keeps its previous behavior.
2221
+ /// scalar-backed text. The schema post-pass resolves inherited simpleContent
2222
+ /// to its terminal scalar while retaining named simple-type constraints.
2223
2223
  fn emit_text_content_parse(
2224
2224
  &self,
2225
2225
  out: &mut String,
@@ -422,7 +422,12 @@ impl TypeScriptCodegen {
422
422
  let mut seen = HashSet::new();
423
423
  for v in &e.variants {
424
424
  let variant_key = self.unique_variant_name(&v.name, &mut seen);
425
- let _ = writeln!(out, " {}: {:?},", variant_key, v.value);
425
+ let _ = writeln!(
426
+ out,
427
+ " {}: {},",
428
+ variant_key,
429
+ super::string_literal(&v.value)
430
+ );
426
431
  }
427
432
  out.push_str("} as const;\n\n");
428
433
 
@@ -437,7 +442,7 @@ impl TypeScriptCodegen {
437
442
  let literals = e
438
443
  .variants
439
444
  .iter()
440
- .map(|v| format!("{:?}", v.value))
445
+ .map(|v| super::string_literal(&v.value))
441
446
  .collect::<Vec<_>>()
442
447
  .join(", ");
443
448
 
@@ -461,7 +466,7 @@ impl TypeScriptCodegen {
461
466
  let tb_literals = e
462
467
  .variants
463
468
  .iter()
464
- .map(|v| format!("Type.Literal({:?})", v.value))
469
+ .map(|v| format!("Type.Literal({})", super::string_literal(&v.value)))
465
470
  .collect::<Vec<_>>()
466
471
  .join(", ");
467
472
  let _ = writeln!(
@@ -605,7 +610,12 @@ impl TypeScriptCodegen {
605
610
  self.emit_docstring(out, doc, "");
606
611
  }
607
612
 
608
- let base_name = s.base_type.as_ref().map(type_ident);
613
+ // Scalar simpleContent bases are text types, not object inheritance.
614
+ let base_name = s
615
+ .base_type
616
+ .as_ref()
617
+ .filter(|name| matches!(ir.types.get(*name), Some(TypeDef::Struct(_))))
618
+ .map(type_ident);
609
619
  let mut inherited_fields = HashSet::new();
610
620
  let mut current_base = s.base_type.as_ref();
611
621
  let mut visited_bases = HashSet::new();
@@ -42,9 +42,7 @@ impl ValueConverter {
42
42
  && facets.min_length.is_none_or(|n| length >= n)
43
43
  && facets.max_length.is_none_or(|n| length <= n);
44
44
  for pattern in &facets.patterns {
45
- valid &= regex::Regex::new(&format!("\\A(?:{pattern})\\z"))
46
- .map(|r| r.is_match(text))
47
- .unwrap_or(false);
45
+ valid &= crate::pattern::matches(pattern, text);
48
46
  }
49
47
  for (bound, inclusive, minimum) in [
50
48
  (&facets.min_inclusive, true, true),
@@ -120,11 +118,7 @@ impl ValueConverter {
120
118
  ScalarType::Pattern(base, patterns) => {
121
119
  let value = std::str::from_utf8(trim_bytes(bytes))?;
122
120
  for pattern in patterns {
123
- let anchored = format!(r"\A(?:{pattern})\z");
124
- if !regex::Regex::new(&anchored)
125
- .map(|compiled| compiled.is_match(value))
126
- .unwrap_or(false)
127
- {
121
+ if !crate::pattern::matches(pattern, value) {
128
122
  return Err(PolyXmlError::ScalarParseError {
129
123
  field: field_name.to_string(),
130
124
  expected: "pattern",
@@ -5,6 +5,7 @@ pub mod integer;
5
5
  pub mod ir;
6
6
  pub mod json;
7
7
  pub mod parser;
8
+ mod pattern;
8
9
  pub mod schema;
9
10
  pub mod schema_parser;
10
11
  pub mod serializer;