polyxml 0.34.4__tar.gz → 0.34.5__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.34.4 → polyxml-0.34.5}/Cargo.lock +6 -6
- {polyxml-0.34.4 → polyxml-0.34.5}/Cargo.toml +1 -1
- {polyxml-0.34.4 → polyxml-0.34.5}/PKG-INFO +1 -1
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/src/codegen/csharp/mod.rs +232 -55
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/src/codegen/go/mod.rs +1 -7
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/src/codegen/rust/mod.rs +15 -1
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_csharp_codegen.rs +4 -1
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_go_codegen.rs +29 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_qualified_attributes.rs +92 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_rust_codegen.rs +38 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/pyproject.toml +1 -1
- {polyxml-0.34.4 → polyxml-0.34.5}/README.md +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/Cargo.toml +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/README.md +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/benches/core_benchmarks.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/benches/support/tag_dispatch_fixtures.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/benches/tag_dispatch.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/src/codegen/cpp/mod.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/src/codegen/java/codec.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/src/codegen/java/mod.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/src/codegen/java/models.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/src/codegen/mod.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/src/codegen/python/aot.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/src/codegen/python/mod.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/src/codegen/typescript/mod.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/src/converters.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/src/error.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/src/integer.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/src/ir/chunker.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/src/ir/mod.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/src/ir/particle.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/src/ir/tarjan.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/src/json.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/src/lib.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/src/parser.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/src/schema.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/src/schema_parser/mod.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/src/schema_parser/upa.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/src/serializer.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/src/transcoder.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/src/value.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/fixtures/lexical_union.xsd +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/fixtures/mixed_content.xsd +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/fixtures/ts_mixed_content_inheritance.xsd +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_adversarial_identifiers.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_any_attribute.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_any_element.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_conformance.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_core.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_cpp_codegen.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_java_codegen.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_json.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_lexical_lists.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_lexical_union_codegen.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_mixed_content.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_pattern_codegen.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_python_codegen.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_rust_codecs.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_schema_audit.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_schema_ir.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_security.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_temporal_union.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_transcoder.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_ts_codegen.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_ubl_csharp.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_unbounded_choice.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_upa.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_value_constraints.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-core/tests/test_xsi_type.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-python/Cargo.toml +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-python/README.md +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-python/benches/aot_vs_dataclass.py +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-python/src/lib.rs +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-python/tests/test_conformance.py +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-python/tests/test_generated_models.py +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-python/tests/test_json.py +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-python/tests/test_mixed_content.py +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-python/tests/test_polyxml.py +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/crates/polyxml-python/tests/test_xsi_type.py +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/python/polyxml/AGENT_GUIDE.md +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/python/polyxml/__init__.py +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/python/polyxml/compat/__init__.py +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/python/polyxml/compat/xsdata.py +0 -0
- {polyxml-0.34.4 → polyxml-0.34.5}/python/polyxml/py.typed +0 -0
|
@@ -608,7 +608,7 @@ dependencies = [
|
|
|
608
608
|
|
|
609
609
|
[[package]]
|
|
610
610
|
name = "polyxml"
|
|
611
|
-
version = "0.34.
|
|
611
|
+
version = "0.34.5"
|
|
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.
|
|
632
|
+
version = "0.34.5"
|
|
633
633
|
dependencies = [
|
|
634
634
|
"polyxml",
|
|
635
635
|
]
|
|
636
636
|
|
|
637
637
|
[[package]]
|
|
638
638
|
name = "polyxml-cli"
|
|
639
|
-
version = "0.34.
|
|
639
|
+
version = "0.34.5"
|
|
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.
|
|
653
|
+
version = "0.34.5"
|
|
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.
|
|
663
|
+
version = "0.34.5"
|
|
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.
|
|
673
|
+
version = "0.34.5"
|
|
674
674
|
dependencies = [
|
|
675
675
|
"polyxml",
|
|
676
676
|
"quick-xml",
|
|
@@ -277,7 +277,7 @@ fn root_names(ir: &SchemaIR) -> HashMap<QName, String> {
|
|
|
277
277
|
names
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
-
fn
|
|
280
|
+
fn choice_variant_base(branch: &UnionBranch) -> String {
|
|
281
281
|
let name = to_csharp_type_name(&branch.variant_name);
|
|
282
282
|
if matches!(
|
|
283
283
|
name.as_str(),
|
|
@@ -290,6 +290,33 @@ fn choice_variant_name(branch: &UnionBranch) -> String {
|
|
|
290
290
|
}
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
+
fn choice_variant_names(union: &UnionDef) -> Vec<String> {
|
|
294
|
+
let mut used = HashSet::from([type_ident(&union.qname)]);
|
|
295
|
+
for branch in &union.branches {
|
|
296
|
+
let mut ty = &branch.type_ref;
|
|
297
|
+
while let TypeRef::List(inner) | TypeRef::Boxed(inner) = ty {
|
|
298
|
+
ty = inner;
|
|
299
|
+
}
|
|
300
|
+
if let TypeRef::Named(name) = ty {
|
|
301
|
+
used.insert(type_ident(name));
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
union
|
|
305
|
+
.branches
|
|
306
|
+
.iter()
|
|
307
|
+
.map(|branch| {
|
|
308
|
+
let base = choice_variant_base(branch);
|
|
309
|
+
let mut name = base.clone();
|
|
310
|
+
let mut suffix = 2;
|
|
311
|
+
while !used.insert(name.clone()) {
|
|
312
|
+
name = format!("{base}{suffix}");
|
|
313
|
+
suffix += 1;
|
|
314
|
+
}
|
|
315
|
+
name
|
|
316
|
+
})
|
|
317
|
+
.collect()
|
|
318
|
+
}
|
|
319
|
+
|
|
293
320
|
fn simple_value_target(base: &TypeRef, ir: &SchemaIR) -> String {
|
|
294
321
|
let mut target = "Value".to_string();
|
|
295
322
|
let mut current = base;
|
|
@@ -298,6 +325,10 @@ fn simple_value_target(base: &TypeRef, ir: &SchemaIR) -> String {
|
|
|
298
325
|
if !seen.insert(qname.clone()) {
|
|
299
326
|
break;
|
|
300
327
|
}
|
|
328
|
+
if matches!(ir.types.get(qname), Some(TypeDef::Enum(_))) {
|
|
329
|
+
target.push_str(".ToXmlValue()");
|
|
330
|
+
break;
|
|
331
|
+
}
|
|
301
332
|
let Some(TypeDef::Simple(simple)) = ir.types.get(qname) else {
|
|
302
333
|
break;
|
|
303
334
|
};
|
|
@@ -340,6 +371,41 @@ impl CSharpCodegen {
|
|
|
340
371
|
|
|
341
372
|
/// Emits all types in the given SchemaIR as a single C# compilation unit.
|
|
342
373
|
pub fn generate_module(&self, ir: &SchemaIR) -> String {
|
|
374
|
+
let has_duplicate_items = ir.types.values().any(|def| {
|
|
375
|
+
let TypeDef::Union(union) = def else {
|
|
376
|
+
return false;
|
|
377
|
+
};
|
|
378
|
+
if !union.is_mixed_content() {
|
|
379
|
+
return false;
|
|
380
|
+
}
|
|
381
|
+
let mut seen = HashSet::new();
|
|
382
|
+
union
|
|
383
|
+
.branches
|
|
384
|
+
.iter()
|
|
385
|
+
.any(|b| !seen.insert((&b.xml_name, &b.namespace, &b.type_ref)))
|
|
386
|
+
});
|
|
387
|
+
let mut canonical = if has_duplicate_items {
|
|
388
|
+
std::borrow::Cow::Owned(ir.clone())
|
|
389
|
+
} else {
|
|
390
|
+
std::borrow::Cow::Borrowed(ir)
|
|
391
|
+
};
|
|
392
|
+
if let std::borrow::Cow::Owned(canonical) = &mut canonical {
|
|
393
|
+
for def in canonical.types.values_mut() {
|
|
394
|
+
if let TypeDef::Union(union) = def {
|
|
395
|
+
if union.is_mixed_content() {
|
|
396
|
+
let mut seen = HashSet::new();
|
|
397
|
+
union.branches.retain(|b| {
|
|
398
|
+
seen.insert((
|
|
399
|
+
b.xml_name.clone(),
|
|
400
|
+
b.namespace.clone(),
|
|
401
|
+
b.type_ref.clone(),
|
|
402
|
+
))
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
let ir = canonical.as_ref();
|
|
343
409
|
set_type_name_map(build_type_name_map(ir, to_csharp_type_name));
|
|
344
410
|
let mut out = String::new();
|
|
345
411
|
|
|
@@ -438,6 +504,13 @@ impl CSharpCodegen {
|
|
|
438
504
|
writeln!(out, "}}").unwrap();
|
|
439
505
|
}
|
|
440
506
|
|
|
507
|
+
if out.contains(".Select(") || out.contains(".Any(") {
|
|
508
|
+
out = out.replacen(
|
|
509
|
+
"using System.Collections.Generic;",
|
|
510
|
+
"using System.Collections.Generic;\nusing System.Linq;",
|
|
511
|
+
1,
|
|
512
|
+
);
|
|
513
|
+
}
|
|
441
514
|
out
|
|
442
515
|
}
|
|
443
516
|
|
|
@@ -749,8 +822,7 @@ impl CSharpCodegen {
|
|
|
749
822
|
|
|
750
823
|
// XmlInclude attributes for element-choice polymorphism.
|
|
751
824
|
if self.options.emit_xml_attributes && !u.is_lexical() && !u.is_mixed_content() {
|
|
752
|
-
for
|
|
753
|
-
let variant_name = choice_variant_name(branch);
|
|
825
|
+
for variant_name in choice_variant_names(u) {
|
|
754
826
|
writeln!(
|
|
755
827
|
out,
|
|
756
828
|
"{}[XmlInclude(typeof({}.{}))]",
|
|
@@ -761,14 +833,11 @@ impl CSharpCodegen {
|
|
|
761
833
|
}
|
|
762
834
|
|
|
763
835
|
if !self.options.use_records && self.options.emit_json_attributes {
|
|
764
|
-
for branch in
|
|
836
|
+
for (branch, variant_name) in u.branches.iter().zip(choice_variant_names(u)) {
|
|
765
837
|
writeln!(
|
|
766
838
|
out,
|
|
767
839
|
"{}[JsonDerivedType(typeof({}.{}), {:?})]",
|
|
768
|
-
indent,
|
|
769
|
-
choice_name,
|
|
770
|
-
choice_variant_name(branch),
|
|
771
|
-
branch.xml_name
|
|
840
|
+
indent, choice_name, variant_name, branch.xml_name
|
|
772
841
|
)
|
|
773
842
|
.unwrap();
|
|
774
843
|
}
|
|
@@ -787,8 +856,7 @@ impl CSharpCodegen {
|
|
|
787
856
|
.unwrap();
|
|
788
857
|
writeln!(out, "{}{{", indent).unwrap();
|
|
789
858
|
|
|
790
|
-
for branch in
|
|
791
|
-
let variant_name = choice_variant_name(branch);
|
|
859
|
+
for (branch, variant_name) in u.branches.iter().zip(choice_variant_names(u)) {
|
|
792
860
|
let branch_type = self.context.map_type_ref(&branch.type_ref);
|
|
793
861
|
|
|
794
862
|
if let Some(ref doc) = branch.documentation {
|
|
@@ -864,8 +932,11 @@ impl CSharpCodegen {
|
|
|
864
932
|
);
|
|
865
933
|
let _ = writeln!(out, "{} {{", indent);
|
|
866
934
|
let _ = writeln!(out, "{} var value = text.Trim();", indent);
|
|
867
|
-
|
|
868
|
-
|
|
935
|
+
let mut exhaustive = false;
|
|
936
|
+
for (idx, (branch, variant_name)) in
|
|
937
|
+
u.branches.iter().zip(choice_variant_names(u)).enumerate()
|
|
938
|
+
{
|
|
939
|
+
let variant = variant_name;
|
|
869
940
|
let mapped = self.context.map_type_ref(&branch.type_ref);
|
|
870
941
|
if let TypeRef::Named(qname) = &branch.type_ref {
|
|
871
942
|
if let Some(TypeDef::Enum(def)) = ir.types.get(qname) {
|
|
@@ -894,6 +965,11 @@ impl CSharpCodegen {
|
|
|
894
965
|
} else {
|
|
895
966
|
format!("candidate{}", idx)
|
|
896
967
|
};
|
|
968
|
+
if matches!(base, TypeRef::Named(q) if matches!(ir.types.get(q), Some(TypeDef::Union(nested)) if nested.is_lexical()))
|
|
969
|
+
{
|
|
970
|
+
writeln!(out, "{indent} try {{ var candidate{idx}={base_name}.Parse(value);return new {variant}({wrap}); }}catch(FormatException){{}}").unwrap();
|
|
971
|
+
continue;
|
|
972
|
+
}
|
|
897
973
|
if let Some(p) = super::unbounded_integer(&branch.type_ref, ir) {
|
|
898
974
|
let kind = match p {
|
|
899
975
|
PrimitiveType::PositiveInteger => 1,
|
|
@@ -927,6 +1003,8 @@ impl CSharpCodegen {
|
|
|
927
1003
|
"{} {{ var candidate{} = value; return new {}({}); }}",
|
|
928
1004
|
indent, idx, variant, wrap
|
|
929
1005
|
);
|
|
1006
|
+
exhaustive = true;
|
|
1007
|
+
break;
|
|
930
1008
|
}
|
|
931
1009
|
} else if base_name == "bool" {
|
|
932
1010
|
let _ = writeln!(out, "{} if (value == \"true\" || value == \"1\") {{ var candidate{} = true; return new {}({}); }}", indent, idx, variant, wrap);
|
|
@@ -943,16 +1021,18 @@ impl CSharpCodegen {
|
|
|
943
1021
|
);
|
|
944
1022
|
}
|
|
945
1023
|
}
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
1024
|
+
if !exhaustive {
|
|
1025
|
+
let _ = writeln!(
|
|
1026
|
+
out,
|
|
1027
|
+
"{} throw new FormatException(\"No {} union member accepts the value\");",
|
|
1028
|
+
indent, name
|
|
1029
|
+
);
|
|
1030
|
+
}
|
|
951
1031
|
let _ = writeln!(out, "{} }}", indent);
|
|
952
1032
|
let _ = writeln!(out, "{indent} private string? _lexical;\n{indent} private string? _parsedCanonical;\n{indent} private static {name} Preserve({name} member, string lexical) {{ member._parsedCanonical = member.FormatXmlValue(); member._lexical = lexical; return member; }}\n{indent} public string ToXmlString() {{ var canonical = FormatXmlValue(); return canonical == _parsedCanonical ? _lexical! : canonical; }}\n{indent} private string FormatXmlValue() => this switch");
|
|
953
1033
|
let _ = writeln!(out, "{} {{", indent);
|
|
954
|
-
for branch in
|
|
955
|
-
let variant =
|
|
1034
|
+
for (branch, variant_name) in u.branches.iter().zip(choice_variant_names(u)) {
|
|
1035
|
+
let variant = variant_name;
|
|
956
1036
|
let value = if matches!(
|
|
957
1037
|
super::primitive_base(&branch.type_ref, ir),
|
|
958
1038
|
TypeRef::Primitive(PrimitiveType::Date)
|
|
@@ -970,6 +1050,7 @@ impl CSharpCodegen {
|
|
|
970
1050
|
"item.Value.ToString(\"HH:mm:ssK\", System.Globalization.CultureInfo.InvariantCulture)".to_string()
|
|
971
1051
|
} else {
|
|
972
1052
|
match &branch.type_ref {
|
|
1053
|
+
TypeRef::Named(qname) if matches!(ir.types.get(qname), Some(TypeDef::Union(nested)) if nested.is_lexical()) => "item.Value.ToXmlString()".to_string(),
|
|
973
1054
|
TypeRef::Named(qname) if matches!(ir.types.get(qname), Some(TypeDef::Enum(_))) => "item.Value.ToXmlValue()".to_string(),
|
|
974
1055
|
TypeRef::Named(qname) if matches!(ir.types.get(qname), Some(TypeDef::Simple(_))) => "Convert.ToString(item.Value.Value, System.Globalization.CultureInfo.InvariantCulture) ?? \"\"".to_string(),
|
|
975
1056
|
_ => "Convert.ToString(item.Value, System.Globalization.CultureInfo.InvariantCulture) ?? \"\"".to_string(),
|
|
@@ -1031,17 +1112,30 @@ impl CSharpCodegen {
|
|
|
1031
1112
|
"{indent} [XmlIgnore] {json}public {ty} {name} {{get;set;}} = {default};"
|
|
1032
1113
|
)
|
|
1033
1114
|
.unwrap();
|
|
1115
|
+
let optional = ty.ends_with('?');
|
|
1034
1116
|
let validation = if field.cardinality.is_list() {
|
|
1035
|
-
|
|
1036
|
-
|
|
1117
|
+
if optional {
|
|
1118
|
+
format!("{name}?.Select(v=>PolyxmlIntegerLexical.Validate(v,{kind})).ToList()")
|
|
1119
|
+
} else {
|
|
1120
|
+
format!("{name}.Select(v=>PolyxmlIntegerLexical.Validate(v,{kind})).ToList()")
|
|
1121
|
+
}
|
|
1122
|
+
} else if optional {
|
|
1037
1123
|
format!("{name} is null ? null : PolyxmlIntegerLexical.Validate({name},{kind})")
|
|
1124
|
+
} else {
|
|
1125
|
+
format!("PolyxmlIntegerLexical.Validate({name} ?? throw new InvalidOperationException(\"Missing required integer\"),{kind})")
|
|
1038
1126
|
};
|
|
1039
1127
|
let parse = if field.cardinality.is_list() {
|
|
1040
|
-
|
|
1128
|
+
if optional {
|
|
1129
|
+
format!("value?.Select(v=>PolyxmlIntegerLexical.Validate(v,{kind})).ToList()")
|
|
1130
|
+
} else {
|
|
1131
|
+
format!("value.Select(v=>PolyxmlIntegerLexical.Validate(v,{kind})).ToList()")
|
|
1132
|
+
}
|
|
1133
|
+
} else if optional {
|
|
1134
|
+
format!("value is null ? null : PolyxmlIntegerLexical.Validate(value,{kind})")
|
|
1041
1135
|
} else {
|
|
1042
|
-
format!("
|
|
1136
|
+
format!("PolyxmlIntegerLexical.Validate(value,{kind})")
|
|
1043
1137
|
};
|
|
1044
|
-
writeln!(out,"{indent} {attrs}{ignore}public {ty} {name}Xml {{ get => {validation}
|
|
1138
|
+
writeln!(out,"{indent} {attrs}{ignore}public {ty} {name}Xml {{ get => {validation}; set => {name} = {parse}; }}").unwrap();
|
|
1045
1139
|
true
|
|
1046
1140
|
}
|
|
1047
1141
|
|
|
@@ -1166,6 +1260,22 @@ impl CSharpCodegen {
|
|
|
1166
1260
|
}
|
|
1167
1261
|
let ty = self.map_field_type(field, ir);
|
|
1168
1262
|
let base = ty.trim_end_matches('?');
|
|
1263
|
+
let can_be_null = ty.ends_with('?')
|
|
1264
|
+
|| !(matches!(
|
|
1265
|
+
base,
|
|
1266
|
+
"bool"
|
|
1267
|
+
| "byte"
|
|
1268
|
+
| "sbyte"
|
|
1269
|
+
| "short"
|
|
1270
|
+
| "ushort"
|
|
1271
|
+
| "int"
|
|
1272
|
+
| "uint"
|
|
1273
|
+
| "long"
|
|
1274
|
+
| "ulong"
|
|
1275
|
+
| "float"
|
|
1276
|
+
| "double"
|
|
1277
|
+
| "decimal"
|
|
1278
|
+
) || matches!(&field.type_ref, TypeRef::Named(q) if matches!(ir.types.get(q), Some(TypeDef::Enum(_)))));
|
|
1169
1279
|
let namespace = field
|
|
1170
1280
|
.namespace
|
|
1171
1281
|
.as_ref()
|
|
@@ -1199,8 +1309,13 @@ impl CSharpCodegen {
|
|
|
1199
1309
|
} else {
|
|
1200
1310
|
format!("object.Equals(value, {expected})")
|
|
1201
1311
|
};
|
|
1312
|
+
let present = if ty.ends_with('?') {
|
|
1313
|
+
"value is not null && "
|
|
1314
|
+
} else {
|
|
1315
|
+
""
|
|
1316
|
+
};
|
|
1202
1317
|
writeln!(out, "{indent} private {ty} _{name} = {initial};
|
|
1203
|
-
{indent} [XmlIgnore] public {ty} {name} {{ get => _{name}; set {{ if (
|
|
1318
|
+
{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();
|
|
1204
1319
|
} else {
|
|
1205
1320
|
let initial = if field.kind == FieldKind::Attribute {
|
|
1206
1321
|
parse(&format!("{default:?}"))
|
|
@@ -1224,10 +1339,15 @@ impl CSharpCodegen {
|
|
|
1224
1339
|
field.xml_name
|
|
1225
1340
|
)
|
|
1226
1341
|
.unwrap();
|
|
1342
|
+
let null_guard = if can_be_null {
|
|
1343
|
+
format!("if ({name} is null) return null; ")
|
|
1344
|
+
} else {
|
|
1345
|
+
String::new()
|
|
1346
|
+
};
|
|
1227
1347
|
if let Some(kind) = integer_kind {
|
|
1228
|
-
writeln!(out,"{indent} get {{
|
|
1348
|
+
writeln!(out,"{indent} get {{ {null_guard} return PolyxmlIntegerLexical.Validate({name},{kind}); }}").unwrap();
|
|
1229
1349
|
} else {
|
|
1230
|
-
writeln!(out, "{indent} get {{
|
|
1350
|
+
writeln!(out, "{indent} get {{ {null_guard}var writer = new System.IO.StringWriter(System.Globalization.CultureInfo.InvariantCulture); new XmlSerializer(typeof({base}), new XmlRootAttribute(\"value\")).Serialize(writer, {name}); return System.Xml.Linq.XDocument.Parse(writer.ToString()).Root!.Value; }}").unwrap();
|
|
1231
1351
|
}
|
|
1232
1352
|
let text = if field.kind == FieldKind::Attribute {
|
|
1233
1353
|
"value".into()
|
|
@@ -1274,12 +1394,24 @@ impl CSharpCodegen {
|
|
|
1274
1394
|
};
|
|
1275
1395
|
let item_name = &prop_names[item_index];
|
|
1276
1396
|
let union_name = type_ident(&union.qname);
|
|
1397
|
+
let text_variant = union
|
|
1398
|
+
.branches
|
|
1399
|
+
.iter()
|
|
1400
|
+
.zip(choice_variant_names(union))
|
|
1401
|
+
.find(|(branch, _)| branch.xml_name == "#text")
|
|
1402
|
+
.map(|(_, name)| name)
|
|
1403
|
+
.expect("mixed union text branch");
|
|
1277
1404
|
let class_kind = if self.options.use_records {
|
|
1278
1405
|
"record"
|
|
1279
1406
|
} else {
|
|
1280
1407
|
"class"
|
|
1281
1408
|
};
|
|
1282
|
-
let
|
|
1409
|
+
let validation_interface = if self.options.emit_validation {
|
|
1410
|
+
", IValidatableObject"
|
|
1411
|
+
} else {
|
|
1412
|
+
""
|
|
1413
|
+
};
|
|
1414
|
+
let _ = writeln!(out, "{indent}public {class_kind} {struct_name} : System.Xml.Serialization.IXmlSerializable{validation_interface}\n{indent}{{");
|
|
1283
1415
|
for (field, prop_name) in s.fields.iter().zip(prop_names) {
|
|
1284
1416
|
let ty = self.map_field_type(field, ir);
|
|
1285
1417
|
if field.kind == FieldKind::Attribute {
|
|
@@ -1307,22 +1439,23 @@ impl CSharpCodegen {
|
|
|
1307
1439
|
if field.kind == FieldKind::Attribute {
|
|
1308
1440
|
let ty = self.map_field_type(field, ir);
|
|
1309
1441
|
let base_type = ty.trim_end_matches('?');
|
|
1310
|
-
let
|
|
1442
|
+
let namespace = field.namespace.as_deref().unwrap_or("");
|
|
1443
|
+
let _ = writeln!(out, " {{ var raw = reader.GetAttribute({:?}, {namespace:?}); if (raw is not null) {prop_name} = ({ty})new XmlSerializer(typeof({base_type}), new XmlRootAttribute(\"value\")).Deserialize(new System.IO.StringReader(\"<value>\" + System.Security.SecurityElement.Escape(raw) + \"</value>\"))!; }}", field.xml_name);
|
|
1311
1444
|
}
|
|
1312
1445
|
}
|
|
1313
1446
|
let _ = writeln!(out, " {item_name} = new List<{union_name}>();");
|
|
1314
1447
|
out.push_str(" bool empty = reader.IsEmptyElement;\n reader.ReadStartElement();\n if (empty) return;\n while (reader.NodeType != System.Xml.XmlNodeType.EndElement && !reader.EOF)\n {\n switch (reader.NodeType)\n {\n case System.Xml.XmlNodeType.Text:\n case System.Xml.XmlNodeType.CDATA:\n case System.Xml.XmlNodeType.Whitespace:\n case System.Xml.XmlNodeType.SignificantWhitespace:\n");
|
|
1315
1448
|
let _ = writeln!(
|
|
1316
1449
|
out,
|
|
1317
|
-
" {item_name}.Add(new {union_name}.
|
|
1450
|
+
" {item_name}.Add(new {union_name}.{text_variant}(reader.Value));"
|
|
1318
1451
|
);
|
|
1319
1452
|
out.push_str(" reader.Read();\n break;\n case System.Xml.XmlNodeType.Element:\n switch (reader.LocalName)\n {\n");
|
|
1320
|
-
for branch in
|
|
1453
|
+
for (branch, variant) in union.branches.iter().zip(choice_variant_names(union)) {
|
|
1321
1454
|
if branch.xml_name == "#text" {
|
|
1322
1455
|
continue;
|
|
1323
1456
|
}
|
|
1324
1457
|
let branch_type = self.context.map_type_ref(&branch.type_ref);
|
|
1325
|
-
|
|
1458
|
+
|
|
1326
1459
|
let namespace = branch.namespace.as_deref().unwrap_or("");
|
|
1327
1460
|
let _ = writeln!(out, " case {:?}:\n {{ var serializer = new XmlSerializer(typeof({branch_type}), new XmlRootAttribute({:?}) {{ Namespace = {:?} }}); {item_name}.Add(new {union_name}.{variant}(({branch_type})serializer.Deserialize(reader)!)); break; }}", branch.xml_name, branch.xml_name, namespace);
|
|
1328
1461
|
}
|
|
@@ -1334,12 +1467,34 @@ impl CSharpCodegen {
|
|
|
1334
1467
|
);
|
|
1335
1468
|
for (field, prop_name) in s.fields.iter().zip(prop_names) {
|
|
1336
1469
|
if field.kind == FieldKind::Attribute {
|
|
1337
|
-
let
|
|
1470
|
+
let ty = self.map_field_type(field, ir);
|
|
1471
|
+
let base = ty.trim_end_matches('?');
|
|
1472
|
+
let is_value = matches!(
|
|
1473
|
+
base,
|
|
1474
|
+
"bool"
|
|
1475
|
+
| "byte"
|
|
1476
|
+
| "sbyte"
|
|
1477
|
+
| "short"
|
|
1478
|
+
| "ushort"
|
|
1479
|
+
| "int"
|
|
1480
|
+
| "uint"
|
|
1481
|
+
| "long"
|
|
1482
|
+
| "ulong"
|
|
1483
|
+
| "float"
|
|
1484
|
+
| "double"
|
|
1485
|
+
| "decimal"
|
|
1486
|
+
) || matches!(&field.type_ref, TypeRef::Named(q) if matches!(ir.types.get(q), Some(TypeDef::Enum(_))));
|
|
1487
|
+
let guard = if ty.ends_with('?') || !is_value {
|
|
1488
|
+
format!("if ({prop_name} is not null) ")
|
|
1489
|
+
} else {
|
|
1490
|
+
String::new()
|
|
1491
|
+
};
|
|
1492
|
+
let namespace = field.namespace.as_deref().unwrap_or("");
|
|
1493
|
+
let _ = writeln!(out, " {guard}{{ var text = new System.IO.StringWriter(System.Globalization.CultureInfo.InvariantCulture); new XmlSerializer(typeof({base}), new XmlRootAttribute(\"value\")).Serialize(text, {prop_name}); writer.WriteAttributeString({:?}, {namespace:?}, System.Xml.Linq.XDocument.Parse(text.ToString()).Root!.Value); }}", field.xml_name);
|
|
1338
1494
|
}
|
|
1339
1495
|
}
|
|
1340
1496
|
let _ = writeln!(out, " foreach (var item in {item_name})\n {{\n switch (item)\n {{");
|
|
1341
|
-
for branch in
|
|
1342
|
-
let variant = choice_variant_name(branch);
|
|
1497
|
+
for (branch, variant) in union.branches.iter().zip(choice_variant_names(union)) {
|
|
1343
1498
|
if branch.xml_name == "#text" {
|
|
1344
1499
|
let _ = writeln!(out, " case {union_name}.{variant} text: writer.WriteString(text.Value); break;");
|
|
1345
1500
|
} else {
|
|
@@ -1349,10 +1504,27 @@ impl CSharpCodegen {
|
|
|
1349
1504
|
}
|
|
1350
1505
|
}
|
|
1351
1506
|
out.push_str(" }\n }\n");
|
|
1352
|
-
let _ = writeln!(out, "{indent} }}
|
|
1507
|
+
let _ = writeln!(out, "{indent} }}");
|
|
1508
|
+
if self.options.emit_validation {
|
|
1509
|
+
self.emit_struct_validator(out, s, ir, prop_names, indent);
|
|
1510
|
+
}
|
|
1511
|
+
let _ = writeln!(out, "{indent}}}\n");
|
|
1353
1512
|
}
|
|
1354
1513
|
|
|
1355
1514
|
fn emit_struct(&self, out: &mut String, s: &StructDef, ir: &SchemaIR, indent: &str) {
|
|
1515
|
+
if ir.has_ordered_content(s) && s.base_type.is_some() {
|
|
1516
|
+
let fields = super::flatten_fields(s, ir);
|
|
1517
|
+
if fields.iter().any(|f| {
|
|
1518
|
+
matches!(&f.type_ref, TypeRef::Named(q)
|
|
1519
|
+
if matches!(ir.types.get(q), Some(TypeDef::Union(u)) if u.is_mixed_content()))
|
|
1520
|
+
}) {
|
|
1521
|
+
let mut flat = s.clone();
|
|
1522
|
+
flat.fields = fields.into_iter().cloned().collect();
|
|
1523
|
+
flat.base_type = None;
|
|
1524
|
+
self.emit_struct(out, &flat, ir, indent);
|
|
1525
|
+
return;
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1356
1528
|
// Reuse inherited XML members. Simple-content IR includes a text
|
|
1357
1529
|
// placeholder for non-inheriting targets; restrictions can repeat
|
|
1358
1530
|
// attribute declarations already present on the generated base.
|
|
@@ -1436,7 +1608,13 @@ impl CSharpCodegen {
|
|
|
1436
1608
|
.map(|f| self.unique_property_name(&f.name, &mut seen_props, Some(&struct_name)))
|
|
1437
1609
|
.collect();
|
|
1438
1610
|
|
|
1439
|
-
if ir.has_ordered_content(s)
|
|
1611
|
+
if ir.has_ordered_content(s)
|
|
1612
|
+
&& self.options.emit_xml_attributes
|
|
1613
|
+
&& s.fields.iter().any(|f| {
|
|
1614
|
+
matches!(&f.type_ref, TypeRef::Named(q)
|
|
1615
|
+
if matches!(ir.types.get(q), Some(TypeDef::Union(u)) if u.is_mixed_content()))
|
|
1616
|
+
})
|
|
1617
|
+
{
|
|
1440
1618
|
self.emit_mixed_struct(out, s, ir, indent, &struct_name, &prop_names);
|
|
1441
1619
|
return;
|
|
1442
1620
|
}
|
|
@@ -1769,16 +1947,15 @@ impl CSharpCodegen {
|
|
|
1769
1947
|
.as_ref()
|
|
1770
1948
|
.map(|b| matches!(ir.types.get(b), Some(TypeDef::Struct(_))))
|
|
1771
1949
|
.unwrap_or(false);
|
|
1772
|
-
let new_kw = if
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
}
|
|
1950
|
+
let new_kw = if self.options.use_records
|
|
1951
|
+
&& self.options.record_kind == CSharpRecordKind::Struct
|
|
1952
|
+
&& !ir.has_ordered_content(s)
|
|
1953
|
+
{
|
|
1954
|
+
""
|
|
1778
1955
|
} else if has_struct_base {
|
|
1779
|
-
"
|
|
1956
|
+
"override "
|
|
1780
1957
|
} else {
|
|
1781
|
-
""
|
|
1958
|
+
"virtual "
|
|
1782
1959
|
};
|
|
1783
1960
|
writeln!(
|
|
1784
1961
|
out,
|
|
@@ -1788,7 +1965,7 @@ impl CSharpCodegen {
|
|
|
1788
1965
|
.unwrap();
|
|
1789
1966
|
writeln!(out, "{} {{", indent).unwrap();
|
|
1790
1967
|
|
|
1791
|
-
if
|
|
1968
|
+
if has_struct_base {
|
|
1792
1969
|
writeln!(out, "{} foreach (var result in base.Validate(validationContext)) yield return result;", indent).unwrap();
|
|
1793
1970
|
}
|
|
1794
1971
|
let mut has_checks = false;
|
|
@@ -1872,11 +2049,12 @@ impl CSharpCodegen {
|
|
|
1872
2049
|
target: &str,
|
|
1873
2050
|
indent: &str,
|
|
1874
2051
|
) {
|
|
2052
|
+
let member = target.split('.').next().unwrap_or(target);
|
|
1875
2053
|
if let Some(min_len) = facets.min_length {
|
|
1876
2054
|
writeln!(
|
|
1877
2055
|
out,
|
|
1878
2056
|
"{}if ({}.Length < {}) yield return new ValidationResult(\"{} length must be >= {}\", [nameof({})]);",
|
|
1879
|
-
indent, target, min_len, target, min_len,
|
|
2057
|
+
indent, target, min_len, target, min_len, member
|
|
1880
2058
|
)
|
|
1881
2059
|
.unwrap();
|
|
1882
2060
|
}
|
|
@@ -1884,7 +2062,7 @@ impl CSharpCodegen {
|
|
|
1884
2062
|
writeln!(
|
|
1885
2063
|
out,
|
|
1886
2064
|
"{}if ({}.Length > {}) yield return new ValidationResult(\"{} length must be <= {}\", [nameof({})]);",
|
|
1887
|
-
indent, target, max_len, target, max_len,
|
|
2065
|
+
indent, target, max_len, target, max_len, member
|
|
1888
2066
|
)
|
|
1889
2067
|
.unwrap();
|
|
1890
2068
|
}
|
|
@@ -1895,7 +2073,7 @@ impl CSharpCodegen {
|
|
|
1895
2073
|
writeln!(
|
|
1896
2074
|
out,
|
|
1897
2075
|
"{}if (!Regex.IsMatch({}.ToString() ?? \"\", \"{}\")) yield return new ValidationResult(\"{} does not match pattern {}\", [nameof({})]);",
|
|
1898
|
-
indent, target, escaped, target, escaped,
|
|
2076
|
+
indent, target, escaped, target, escaped, member
|
|
1899
2077
|
)
|
|
1900
2078
|
.unwrap();
|
|
1901
2079
|
}
|
|
@@ -1903,7 +2081,7 @@ impl CSharpCodegen {
|
|
|
1903
2081
|
writeln!(
|
|
1904
2082
|
out,
|
|
1905
2083
|
"{}if ({} < {}) yield return new ValidationResult(\"{} must be >= {}\", [nameof({})]);",
|
|
1906
|
-
indent, target, min_inc, target, min_inc,
|
|
2084
|
+
indent, target, min_inc, target, min_inc, member
|
|
1907
2085
|
)
|
|
1908
2086
|
.unwrap();
|
|
1909
2087
|
}
|
|
@@ -1911,7 +2089,7 @@ impl CSharpCodegen {
|
|
|
1911
2089
|
writeln!(
|
|
1912
2090
|
out,
|
|
1913
2091
|
"{}if ({} > {}) yield return new ValidationResult(\"{} must be <= {}\", [nameof({})]);",
|
|
1914
|
-
indent, target, max_inc, target, max_inc,
|
|
2092
|
+
indent, target, max_inc, target, max_inc, member
|
|
1915
2093
|
)
|
|
1916
2094
|
.unwrap();
|
|
1917
2095
|
}
|
|
@@ -1919,7 +2097,7 @@ impl CSharpCodegen {
|
|
|
1919
2097
|
writeln!(
|
|
1920
2098
|
out,
|
|
1921
2099
|
"{}if ({} <= {}) yield return new ValidationResult(\"{} must be > {}\", [nameof({})]);",
|
|
1922
|
-
indent, target, min_exc, target, min_exc,
|
|
2100
|
+
indent, target, min_exc, target, min_exc, member
|
|
1923
2101
|
)
|
|
1924
2102
|
.unwrap();
|
|
1925
2103
|
}
|
|
@@ -1927,7 +2105,7 @@ impl CSharpCodegen {
|
|
|
1927
2105
|
writeln!(
|
|
1928
2106
|
out,
|
|
1929
2107
|
"{}if ({} >= {}) yield return new ValidationResult(\"{} must be < {}\", [nameof({})]);",
|
|
1930
|
-
indent, target, max_exc, target, max_exc,
|
|
2108
|
+
indent, target, max_exc, target, max_exc, member
|
|
1931
2109
|
)
|
|
1932
2110
|
.unwrap();
|
|
1933
2111
|
}
|
|
@@ -1984,8 +2162,7 @@ impl CSharpCodegen {
|
|
|
1984
2162
|
if let Some(qname) = named_qname {
|
|
1985
2163
|
if let Some(TypeDef::Union(u)) = ir.types.get(qname) {
|
|
1986
2164
|
let choice_name = type_ident(&u.qname);
|
|
1987
|
-
for branch in
|
|
1988
|
-
let variant_name = choice_variant_name(branch);
|
|
2165
|
+
for (branch, variant_name) in u.branches.iter().zip(choice_variant_names(u)) {
|
|
1989
2166
|
parts.push(format!(
|
|
1990
2167
|
"XmlElement(\"{}\", typeof({}.{}), Namespace = {:?})",
|
|
1991
2168
|
branch.xml_name,
|
|
@@ -419,13 +419,7 @@ impl GoCodegen {
|
|
|
419
419
|
if body.contains("xml.") {
|
|
420
420
|
imports.push("\"encoding/xml\"");
|
|
421
421
|
}
|
|
422
|
-
if
|
|
423
|
-
&& (!ir.content_models.is_empty()
|
|
424
|
-
|| ir
|
|
425
|
-
.types
|
|
426
|
-
.values()
|
|
427
|
-
.any(|def| matches!(def, TypeDef::Struct(s) if ir.has_ordered_content(s))))
|
|
428
|
-
{
|
|
422
|
+
if body.contains("bytes.") {
|
|
429
423
|
imports.push("\"bytes\"");
|
|
430
424
|
}
|
|
431
425
|
if has_fmt || body.contains("fmt.") {
|
|
@@ -345,7 +345,7 @@ impl RustCodegen {
|
|
|
345
345
|
let chunk_size = self.options.chunk_size.unwrap_or(250);
|
|
346
346
|
|
|
347
347
|
let safe_mod = sanitize_rust_module_name(base_name);
|
|
348
|
-
let mod_export = if safe_mod == base_name {
|
|
348
|
+
let mut mod_export = if safe_mod == base_name {
|
|
349
349
|
format!("pub mod {base_name};\npub use {base_name}::*;\n")
|
|
350
350
|
} else {
|
|
351
351
|
format!("#[path = \"{base_name}.rs\"]\npub mod {safe_mod};\npub use {safe_mod}::*;\n")
|
|
@@ -353,6 +353,13 @@ impl RustCodegen {
|
|
|
353
353
|
|
|
354
354
|
if !should_split {
|
|
355
355
|
let code = self.generate_module(ir);
|
|
356
|
+
if !code.lines().any(|line| line.starts_with("pub ")) {
|
|
357
|
+
mod_export = mod_export
|
|
358
|
+
.lines()
|
|
359
|
+
.filter(|line| !line.starts_with("pub use "))
|
|
360
|
+
.map(|line| format!("{line}\n"))
|
|
361
|
+
.collect();
|
|
362
|
+
}
|
|
356
363
|
if base_name == "mod" {
|
|
357
364
|
return vec![("mod.rs".to_string(), code)];
|
|
358
365
|
}
|
|
@@ -365,6 +372,13 @@ impl RustCodegen {
|
|
|
365
372
|
let plan = ir.partition_topological_chunks(chunk_size);
|
|
366
373
|
if plan.chunks.len() <= 1 {
|
|
367
374
|
let code = self.generate_module(ir);
|
|
375
|
+
if !code.lines().any(|line| line.starts_with("pub ")) {
|
|
376
|
+
mod_export = mod_export
|
|
377
|
+
.lines()
|
|
378
|
+
.filter(|line| !line.starts_with("pub use "))
|
|
379
|
+
.map(|line| format!("{line}\n"))
|
|
380
|
+
.collect();
|
|
381
|
+
}
|
|
368
382
|
if base_name == "mod" {
|
|
369
383
|
return vec![("mod.rs".to_string(), code)];
|
|
370
384
|
}
|
|
@@ -79,6 +79,9 @@ static DOTNET_LOCK: Mutex<()> = Mutex::new(());
|
|
|
79
79
|
#[test]
|
|
80
80
|
fn particle_wire_structure_round_trips() {
|
|
81
81
|
for (schema, root, documents) in [
|
|
82
|
+
(r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"><xs:element name="Root"><xs:complexType mixed="true"><xs:sequence><xs:element name="Description" type="xs:string" minOccurs="0"/><xs:element name="Link" type="xs:string"/><xs:element name="Description" type="xs:string" minOccurs="0"/></xs:sequence></xs:complexType></xs:element></xs:schema>"#, "Root", vec!["<Root><Description>first</Description><Link>middle</Link><Description>second</Description></Root>"]),
|
|
83
|
+
(r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"><xs:complexType name="VehicleType"><xs:simpleContent><xs:extension base="xs:string"/></xs:simpleContent></xs:complexType><xs:complexType name="OtherType"><xs:simpleContent><xs:extension base="xs:string"/></xs:simpleContent></xs:complexType><xs:element name="Root"><xs:complexType mixed="true"><xs:choice minOccurs="0" maxOccurs="unbounded"><xs:element name="Vehicle" type="VehicleType"/><xs:element name="VehicleType" type="OtherType"/></xs:choice></xs:complexType></xs:element></xs:schema>"#, "Root", vec!["<Root><Vehicle>one</Vehicle><VehicleType>two</VehicleType></Root>"]),
|
|
84
|
+
(r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"><xs:simpleType name="Phase"><xs:restriction base="xs:string"><xs:enumeration value="on"/><xs:enumeration value="off"/></xs:restriction></xs:simpleType><xs:complexType name="Base" mixed="true"><xs:choice minOccurs="0" maxOccurs="unbounded"><xs:element name="ItemA" type="xs:string"/><xs:element name="item_a" type="xs:string"/></xs:choice><xs:attribute name="code" type="xs:string"/><xs:attribute name="enabled" type="xs:boolean" use="required"/><xs:attribute name="phase" type="Phase" use="required"/></xs:complexType><xs:complexType name="Derived" mixed="true"><xs:complexContent mixed="true"><xs:extension base="Base"><xs:attribute name="extra" type="xs:string"/></xs:extension></xs:complexContent></xs:complexType><xs:element name="Root" type="Derived"/></xs:schema>"#, "Root", vec![r#"<Root code="base" extra="leaf" enabled="true" phase="on">before<ItemA>A</ItemA>between<item_a>B</item_a>after</Root>"#]),
|
|
82
85
|
(r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"><xs:element name="Query"><xs:complexType><xs:sequence><xs:element name="Equals" type="xs:string"/><xs:element name="EqualsValue" type="xs:string"/><xs:element name="GetHashCode" type="xs:string"/><xs:element name="ToString" type="xs:string"/></xs:sequence></xs:complexType></xs:element></xs:schema>"#, "Query", vec!["<Query><Equals>a</Equals><EqualsValue>b</EqualsValue><GetHashCode>c</GetHashCode><ToString>d</ToString></Query>"]),
|
|
83
86
|
(include_str!("../../../research/fixtures/nested_sequence_choice.xsd"), "Root", vec!["<Root><First>A</First><Second>B</Second></Root>", "<Root><Alternative>C</Alternative></Root>"]),
|
|
84
87
|
(include_str!("../../../research/fixtures/repeated_sequence.xsd"), "Root", vec!["<Root><First>A1</First><Second>B1</Second><First>A2</First><Second>B2</Second></Root>"]),
|
|
@@ -98,7 +101,7 @@ using System.Xml.Serialization;
|
|
|
98
101
|
using System.Xml.Linq;
|
|
99
102
|
using ParticleModels;
|
|
100
103
|
var serializer = new XmlSerializer(typeof({root}));
|
|
101
|
-
string Structure(XElement e) => e.Name.ToString()+"
|
|
104
|
+
string Structure(XElement e) => e.Name.ToString()+"("+string.Join(",",e.Attributes().Where(a=>!a.IsNamespaceDeclaration).OrderBy(a=>a.Name.ToString()).Select(a=>a.Name+"="+a.Value))+")["+string.Concat(e.Nodes().Select(n=>n is XElement child ? Structure(child) : n is XText text ? text.Value : ""))+"]";
|
|
102
105
|
foreach (var document in new [] {{ {documents} }}) {{
|
|
103
106
|
var value = serializer.Deserialize(new StringReader(document))!;
|
|
104
107
|
var writer = new StringWriter(); serializer.Serialize(writer, value);
|
|
@@ -1449,3 +1449,32 @@ func TestIntegers(t *testing.T) {
|
|
|
1449
1449
|
String::from_utf8_lossy(&result.stderr)
|
|
1450
1450
|
);
|
|
1451
1451
|
}
|
|
1452
|
+
|
|
1453
|
+
#[test]
|
|
1454
|
+
fn imported_ordered_types_do_not_add_unused_bytes_import() {
|
|
1455
|
+
let mut ir=XsdParser::new().parse_str(r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"><xs:complexType name="Foreign" mixed="true"><xs:sequence><xs:element name="child" type="xs:string"/></xs:sequence></xs:complexType><xs:complexType name="Local"><xs:attribute name="id" type="xs:string"/></xs:complexType></xs:schema>"#).unwrap();
|
|
1456
|
+
for name in ir.types.keys() {
|
|
1457
|
+
if name.local != "Local" {
|
|
1458
|
+
ir.external_types.insert(name.clone(), "shared".into());
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
ir.content_models.clear();
|
|
1462
|
+
let dir = tempdir().unwrap();
|
|
1463
|
+
fs::write(dir.path().join("go.mod"), "module importcheck\n\ngo 1.22\n").unwrap();
|
|
1464
|
+
fs::write(
|
|
1465
|
+
dir.path().join("models.go"),
|
|
1466
|
+
GoCodegen::new(GoOptions::default()).generate_module(&ir),
|
|
1467
|
+
)
|
|
1468
|
+
.unwrap();
|
|
1469
|
+
let result = Command::new("go")
|
|
1470
|
+
.args(["test", "./..."])
|
|
1471
|
+
.current_dir(dir.path())
|
|
1472
|
+
.output()
|
|
1473
|
+
.unwrap();
|
|
1474
|
+
assert!(
|
|
1475
|
+
result.status.success(),
|
|
1476
|
+
"{}{}",
|
|
1477
|
+
String::from_utf8_lossy(&result.stdout),
|
|
1478
|
+
String::from_utf8_lossy(&result.stderr)
|
|
1479
|
+
);
|
|
1480
|
+
}
|
|
@@ -168,3 +168,95 @@ var json=System.Text.Json.JsonSerializer.Serialize(value);if(!json.Contains("att
|
|
|
168
168
|
);
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
|
+
|
|
172
|
+
#[test]
|
|
173
|
+
fn derived_validators_preserve_base_constraints() {
|
|
174
|
+
use polyxml::ir::{QName, RestrictionFacets, TypeDef};
|
|
175
|
+
let mut ir=XsdParser::new().parse_str(r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"><xs:complexType name="Base"><xs:attribute name="code" type="xs:string"/></xs:complexType><xs:complexType name="Derived"><xs:complexContent><xs:extension base="Base"><xs:attribute name="extra" type="xs:string"/></xs:extension></xs:complexContent></xs:complexType></xs:schema>"#).unwrap();
|
|
176
|
+
let TypeDef::Struct(base) = ir.types.get_mut(&QName::local("Base")).unwrap() else {
|
|
177
|
+
panic!()
|
|
178
|
+
};
|
|
179
|
+
base.fields[0].facets = Some(RestrictionFacets {
|
|
180
|
+
min_length: Some(3),
|
|
181
|
+
..Default::default()
|
|
182
|
+
});
|
|
183
|
+
for use_records in [true, false] {
|
|
184
|
+
let temp = tempdir().unwrap();
|
|
185
|
+
fs::write(temp.path().join("App.csproj"),r#"<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><OutputType>Exe</OutputType><TargetFramework>net8.0</TargetFramework><ImplicitUsings>enable</ImplicitUsings><Nullable>enable</Nullable><WarningsAsErrors>CS0108;CS0109;CS0114</WarningsAsErrors></PropertyGroup></Project>"#).unwrap();
|
|
186
|
+
fs::write(
|
|
187
|
+
temp.path().join("Models.cs"),
|
|
188
|
+
CSharpCodegen::new(CSharpOptions {
|
|
189
|
+
namespace: "Models".into(),
|
|
190
|
+
use_records,
|
|
191
|
+
..Default::default()
|
|
192
|
+
})
|
|
193
|
+
.generate_module(&ir),
|
|
194
|
+
)
|
|
195
|
+
.unwrap();
|
|
196
|
+
fs::write(temp.path().join("Program.cs"),r#"using Models;using System.ComponentModel.DataAnnotations;
|
|
197
|
+
var value=new Derived {Code="x",Extra="leaf"};
|
|
198
|
+
if(!value.Validate(new ValidationContext(value)).Any())throw new Exception("base constraint lost");
|
|
199
|
+
if(!((Base)value).Validate(new ValidationContext(value)).Any())throw new Exception("base dispatch lost");
|
|
200
|
+
var results=new List<ValidationResult>();if(Validator.TryValidateObject(value,new ValidationContext(value),results,true))throw new Exception("interface validation lost");
|
|
201
|
+
var valid=new Derived {Code="valid",Extra="leaf"};if(valid.Validate(new ValidationContext(valid)).Any())throw new Exception("valid value rejected");
|
|
202
|
+
"#).unwrap();
|
|
203
|
+
let output = Command::new("dotnet")
|
|
204
|
+
.args(["run", "--disable-build-servers"])
|
|
205
|
+
.current_dir(temp.path())
|
|
206
|
+
.output()
|
|
207
|
+
.unwrap();
|
|
208
|
+
assert!(
|
|
209
|
+
output.status.success(),
|
|
210
|
+
"records={use_records}: {}{}",
|
|
211
|
+
String::from_utf8_lossy(&output.stdout),
|
|
212
|
+
String::from_utf8_lossy(&output.stderr)
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
#[test]
|
|
218
|
+
fn lexical_dependencies_compile_and_round_trip_without_implicit_imports() {
|
|
219
|
+
let ir=XsdParser::new().parse_str(r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
|
220
|
+
<xs:simpleType name="Code"><xs:restriction base="xs:string"><xs:enumeration value="A"/><xs:enumeration value="B"/><xs:enumeration value="LONG"/></xs:restriction></xs:simpleType>
|
|
221
|
+
<xs:simpleType name="ShortCode"><xs:restriction base="Code"><xs:maxLength value="1"/></xs:restriction></xs:simpleType><xs:simpleType name="Inner"><xs:union memberTypes="Code xs:int"/></xs:simpleType>
|
|
222
|
+
<xs:simpleType name="Outer"><xs:union memberTypes="Inner xs:token xs:anyURI"/></xs:simpleType>
|
|
223
|
+
<xs:simpleType name="Numbers"><xs:list itemType="xs:int"/></xs:simpleType>
|
|
224
|
+
<xs:complexType name="Payload"><xs:sequence><xs:element name="Flag" type="xs:boolean" default="true"/><xs:element name="Count" type="xs:int" fixed="7"/><xs:element name="Huge" type="xs:integer"/><xs:element name="Values" type="Numbers"/><xs:element name="Reason" type="Outer"/></xs:sequence><xs:attribute name="mode" type="Code" use="required" fixed="A"/></xs:complexType>
|
|
225
|
+
</xs:schema>"#).unwrap();
|
|
226
|
+
for use_records in [true, false] {
|
|
227
|
+
let temp = tempdir().unwrap();
|
|
228
|
+
fs::write(temp.path().join("App.csproj"),r#"<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><OutputType>Exe</OutputType><TargetFramework>net8.0</TargetFramework><Nullable>enable</Nullable><TreatWarningsAsErrors>true</TreatWarningsAsErrors></PropertyGroup></Project>"#).unwrap();
|
|
229
|
+
fs::write(
|
|
230
|
+
temp.path().join("Models.cs"),
|
|
231
|
+
CSharpCodegen::new(CSharpOptions {
|
|
232
|
+
namespace: "Models".into(),
|
|
233
|
+
use_records,
|
|
234
|
+
..Default::default()
|
|
235
|
+
})
|
|
236
|
+
.generate_module(&ir),
|
|
237
|
+
)
|
|
238
|
+
.unwrap();
|
|
239
|
+
fs::write(temp.path().join("Program.cs"),r#"using System;using System.IO;using System.Xml.Serialization;using System.Xml.Linq;using Models;using System.ComponentModel.DataAnnotations;
|
|
240
|
+
var invalidCode=new ShortCode((Code)2);if(!invalidCode.Validate(new ValidationContext(invalidCode)).GetEnumerator().MoveNext())throw new Exception("enum lexical facet ignored");
|
|
241
|
+
var validCode=new ShortCode(Code.A);if(validCode.Validate(new ValidationContext(validCode)).GetEnumerator().MoveNext())throw new Exception("valid enum rejected");
|
|
242
|
+
foreach(var lexical in new[]{"A","42","fallback"}) {
|
|
243
|
+
var serializer=new XmlSerializer(typeof(Payload));var value=(Payload)serializer.Deserialize(new StringReader("<Payload mode='A'><Flag/><Count>7</Count><Huge>123456789012345678901234567890</Huge><Values>1 2</Values><Reason>"+lexical+"</Reason></Payload>"))!;
|
|
244
|
+
if(!value.Flag||value.Count!=7||value.Huge!="123456789012345678901234567890"||value.Values.Value.Count!=2||value.Values.Value[1]!=2||value.Reason.ToXmlString()!=lexical)throw new Exception("lexical input lost");
|
|
245
|
+
var writer=new StringWriter();serializer.Serialize(writer,value);var root=XDocument.Parse(writer.ToString()).Root!;
|
|
246
|
+
if(root.Attribute("mode")!.Value!="A"||root.Element("Flag")!.Value!="true"||root.Element("Count")!.Value!="7"||root.Element("Values")!.Value!="1 2"||root.Element("Reason")!.Value!=lexical)throw new Exception(writer.ToString());
|
|
247
|
+
value.Huge=null!;try{serializer.Serialize(new StringWriter(),value);throw new Exception("missing required integer emitted");}catch(InvalidOperationException){}
|
|
248
|
+
}
|
|
249
|
+
"#).unwrap();
|
|
250
|
+
let output = Command::new("dotnet")
|
|
251
|
+
.args(["run", "--disable-build-servers"])
|
|
252
|
+
.current_dir(temp.path())
|
|
253
|
+
.output()
|
|
254
|
+
.unwrap();
|
|
255
|
+
assert!(
|
|
256
|
+
output.status.success(),
|
|
257
|
+
"records={use_records}: {}{}",
|
|
258
|
+
String::from_utf8_lossy(&output.stdout),
|
|
259
|
+
String::from_utf8_lossy(&output.stderr)
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
@@ -1423,3 +1423,41 @@ fn main() {
|
|
|
1423
1423
|
);
|
|
1424
1424
|
}
|
|
1425
1425
|
}
|
|
1426
|
+
|
|
1427
|
+
#[test]
|
|
1428
|
+
fn empty_modules_compile_without_unused_reexports() {
|
|
1429
|
+
let core = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
|
|
1430
|
+
for split_units in [false, true] {
|
|
1431
|
+
let dir = tempfile::tempdir().unwrap();
|
|
1432
|
+
std::fs::create_dir_all(dir.path().join("src/models")).unwrap();
|
|
1433
|
+
let ir = SchemaIR::new();
|
|
1434
|
+
for (name, code) in RustCodegen::new(RustOptions {
|
|
1435
|
+
split_units: Some(split_units),
|
|
1436
|
+
..Default::default()
|
|
1437
|
+
})
|
|
1438
|
+
.generate_files(&ir, "models")
|
|
1439
|
+
{
|
|
1440
|
+
std::fs::write(dir.path().join("src/models").join(name), code).unwrap();
|
|
1441
|
+
}
|
|
1442
|
+
std::fs::write(
|
|
1443
|
+
dir.path().join("src/lib.rs"),
|
|
1444
|
+
"#![deny(unused_imports)]\npub mod models;\n",
|
|
1445
|
+
)
|
|
1446
|
+
.unwrap();
|
|
1447
|
+
std::fs::write(dir.path().join("Cargo.toml"),format!("[package]\nname=\"empty-module-test\"\nversion=\"0.0.0\"\nedition=\"2021\"\n[dependencies]\npolyxml={{path={core:?}}}\nquick-xml=\"0.42\"\nserde={{version=\"1\",features=[\"derive\"]}}\nserde_json=\"1\"\n")).unwrap();
|
|
1448
|
+
let result = std::process::Command::new("cargo")
|
|
1449
|
+
.args(["check", "--quiet", "--offline"])
|
|
1450
|
+
.env(
|
|
1451
|
+
"CARGO_TARGET_DIR",
|
|
1452
|
+
core.join("../../target/binary-lexical-tests"),
|
|
1453
|
+
)
|
|
1454
|
+
.current_dir(dir.path())
|
|
1455
|
+
.output()
|
|
1456
|
+
.unwrap();
|
|
1457
|
+
assert!(
|
|
1458
|
+
result.status.success(),
|
|
1459
|
+
"split={split_units}: {}",
|
|
1460
|
+
String::from_utf8_lossy(&result.stderr)
|
|
1461
|
+
);
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{polyxml-0.34.4 → polyxml-0.34.5}/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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|