polyxml 0.30.3__tar.gz → 0.30.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.30.3 → polyxml-0.30.5}/Cargo.lock +6 -6
- {polyxml-0.30.3 → polyxml-0.30.5}/Cargo.toml +1 -1
- {polyxml-0.30.3 → polyxml-0.30.5}/PKG-INFO +1 -1
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/src/codegen/cpp/mod.rs +130 -7
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/src/codegen/go/mod.rs +22 -3
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/src/codegen/typescript/mod.rs +45 -10
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/src/ir/mod.rs +3 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/src/schema_parser/mod.rs +109 -0
- polyxml-0.30.5/crates/polyxml-core/tests/fixtures/ts_mixed_content_inheritance.xsd +17 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/tests/test_cpp_codegen.rs +58 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/tests/test_go_codegen.rs +93 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/tests/test_schema_ir.rs +41 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/tests/test_ts_codegen.rs +29 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/pyproject.toml +1 -1
- {polyxml-0.30.3 → polyxml-0.30.5}/README.md +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/Cargo.toml +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/README.md +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/benches/core_benchmarks.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/benches/support/tag_dispatch_fixtures.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/benches/tag_dispatch.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/src/codegen/csharp/mod.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/src/codegen/java/codec.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/src/codegen/java/mod.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/src/codegen/java/models.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/src/codegen/mod.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/src/codegen/python/aot.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/src/codegen/python/mod.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/src/codegen/rust/mod.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/src/converters.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/src/error.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/src/ir/tarjan.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/src/json.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/src/lib.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/src/parser.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/src/schema.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/src/serializer.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/src/transcoder.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/src/value.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/tests/fixtures/lexical_union.xsd +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/tests/fixtures/mixed_content.xsd +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/tests/test_adversarial_identifiers.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/tests/test_conformance.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/tests/test_core.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/tests/test_csharp_codegen.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/tests/test_java_codegen.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/tests/test_json.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/tests/test_lexical_union_codegen.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/tests/test_mixed_content.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/tests/test_pattern_codegen.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/tests/test_python_codegen.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/tests/test_rust_codecs.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/tests/test_rust_codegen.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/tests/test_schema_audit.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/tests/test_security.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/tests/test_transcoder.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/tests/test_unbounded_choice.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-core/tests/test_xsi_type.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-python/Cargo.toml +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-python/README.md +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-python/benches/aot_vs_dataclass.py +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-python/src/lib.rs +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-python/tests/test_conformance.py +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-python/tests/test_generated_models.py +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-python/tests/test_json.py +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-python/tests/test_mixed_content.py +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-python/tests/test_polyxml.py +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/crates/polyxml-python/tests/test_xsi_type.py +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/python/polyxml/AGENT_GUIDE.md +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/python/polyxml/__init__.py +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/python/polyxml/compat/__init__.py +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/python/polyxml/compat/xsdata.py +0 -0
- {polyxml-0.30.3 → polyxml-0.30.5}/python/polyxml/py.typed +0 -0
|
@@ -608,7 +608,7 @@ dependencies = [
|
|
|
608
608
|
|
|
609
609
|
[[package]]
|
|
610
610
|
name = "polyxml"
|
|
611
|
-
version = "0.30.
|
|
611
|
+
version = "0.30.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.30.
|
|
632
|
+
version = "0.30.5"
|
|
633
633
|
dependencies = [
|
|
634
634
|
"polyxml",
|
|
635
635
|
]
|
|
636
636
|
|
|
637
637
|
[[package]]
|
|
638
638
|
name = "polyxml-cli"
|
|
639
|
-
version = "0.30.
|
|
639
|
+
version = "0.30.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.30.
|
|
653
|
+
version = "0.30.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.30.
|
|
663
|
+
version = "0.30.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.30.
|
|
673
|
+
version = "0.30.5"
|
|
674
674
|
dependencies = [
|
|
675
675
|
"polyxml",
|
|
676
676
|
"quick-xml",
|
|
@@ -674,15 +674,19 @@ concept XmlModel = requires(T a) {{
|
|
|
674
674
|
}
|
|
675
675
|
}
|
|
676
676
|
|
|
677
|
-
//
|
|
677
|
+
// Cut cyclic value dependencies through struct fields or union branches,
|
|
678
|
+
// then emit types in dependency order.
|
|
679
|
+
let mut prepared = ir.clone();
|
|
680
|
+
let sorted_qnames = self.order_with_forward_cuts(&mut prepared);
|
|
681
|
+
|
|
678
682
|
for qname in sorted_qnames {
|
|
679
|
-
if
|
|
683
|
+
if prepared.is_external_type(&qname) {
|
|
680
684
|
continue;
|
|
681
685
|
}
|
|
682
|
-
if let Some(type_def) =
|
|
686
|
+
if let Some(type_def) = prepared.types.get(&qname) {
|
|
683
687
|
match type_def {
|
|
684
688
|
TypeDef::Union(u) => self.emit_union(out, u),
|
|
685
|
-
TypeDef::Struct(s) => self.emit_struct(out, s,
|
|
689
|
+
TypeDef::Struct(s) => self.emit_struct(out, s, &prepared),
|
|
686
690
|
TypeDef::Simple(_) | TypeDef::Enum(_) => {}
|
|
687
691
|
}
|
|
688
692
|
}
|
|
@@ -1126,7 +1130,120 @@ concept XmlModel = requires(T a) {{
|
|
|
1126
1130
|
}
|
|
1127
1131
|
}
|
|
1128
1132
|
|
|
1129
|
-
///
|
|
1133
|
+
/// Box cyclic value edges until the C++ dependency graph can be ordered.
|
|
1134
|
+
fn order_with_forward_cuts(&self, ir: &mut SchemaIR) -> Vec<QName> {
|
|
1135
|
+
loop {
|
|
1136
|
+
let order = self.topological_sort_types(ir);
|
|
1137
|
+
let mut candidate: Option<(bool, QName, usize)> = None;
|
|
1138
|
+
for (owner, def) in &ir.types {
|
|
1139
|
+
let TypeDef::Struct(structure) = def else {
|
|
1140
|
+
continue;
|
|
1141
|
+
};
|
|
1142
|
+
for (index, field) in structure.fields.iter().enumerate() {
|
|
1143
|
+
if field.is_cycle_cut || field.cardinality.is_list() {
|
|
1144
|
+
continue;
|
|
1145
|
+
}
|
|
1146
|
+
let TypeRef::Named(target) = &field.type_ref else {
|
|
1147
|
+
continue;
|
|
1148
|
+
};
|
|
1149
|
+
if !matches!(ir.types.get(target), Some(TypeDef::Struct(_)))
|
|
1150
|
+
|| !self.has_cpp_dependency_path(ir, target, owner, &mut HashSet::new())
|
|
1151
|
+
{
|
|
1152
|
+
continue;
|
|
1153
|
+
}
|
|
1154
|
+
let optional = field.cardinality.is_optional() || field.nillable;
|
|
1155
|
+
if candidate
|
|
1156
|
+
.as_ref()
|
|
1157
|
+
.is_none_or(|(was_optional, _, _)| optional && !was_optional)
|
|
1158
|
+
{
|
|
1159
|
+
candidate = Some((optional, owner.clone(), index));
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
let Some((_, owner, index)) = candidate else {
|
|
1164
|
+
let union_cut = ir.types.iter().find_map(|(owner, def)| {
|
|
1165
|
+
let TypeDef::Union(union) = def else {
|
|
1166
|
+
return None;
|
|
1167
|
+
};
|
|
1168
|
+
union
|
|
1169
|
+
.branches
|
|
1170
|
+
.iter()
|
|
1171
|
+
.enumerate()
|
|
1172
|
+
.find_map(|(index, branch)| {
|
|
1173
|
+
let TypeRef::Named(target) = &branch.type_ref else {
|
|
1174
|
+
return None;
|
|
1175
|
+
};
|
|
1176
|
+
(matches!(ir.types.get(target), Some(TypeDef::Struct(_)))
|
|
1177
|
+
&& self.has_cpp_dependency_path(
|
|
1178
|
+
ir,
|
|
1179
|
+
target,
|
|
1180
|
+
owner,
|
|
1181
|
+
&mut HashSet::new(),
|
|
1182
|
+
))
|
|
1183
|
+
.then(|| (owner.clone(), index))
|
|
1184
|
+
})
|
|
1185
|
+
});
|
|
1186
|
+
let Some((owner, index)) = union_cut else {
|
|
1187
|
+
return order;
|
|
1188
|
+
};
|
|
1189
|
+
if let Some(TypeDef::Union(union)) = ir.types.get_mut(&owner) {
|
|
1190
|
+
let branch = &mut union.branches[index];
|
|
1191
|
+
branch.type_ref = TypeRef::Boxed(Box::new(branch.type_ref.clone()));
|
|
1192
|
+
}
|
|
1193
|
+
continue;
|
|
1194
|
+
};
|
|
1195
|
+
if let Some(TypeDef::Struct(structure)) = ir.types.get_mut(&owner) {
|
|
1196
|
+
structure.fields[index].is_cycle_cut = true;
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
fn has_cpp_dependency_path(
|
|
1202
|
+
&self,
|
|
1203
|
+
ir: &SchemaIR,
|
|
1204
|
+
current: &QName,
|
|
1205
|
+
goal: &QName,
|
|
1206
|
+
seen: &mut HashSet<QName>,
|
|
1207
|
+
) -> bool {
|
|
1208
|
+
if current == goal {
|
|
1209
|
+
return true;
|
|
1210
|
+
}
|
|
1211
|
+
if !seen.insert(current.clone()) {
|
|
1212
|
+
return false;
|
|
1213
|
+
}
|
|
1214
|
+
let mut deps = Vec::new();
|
|
1215
|
+
match ir.types.get(current) {
|
|
1216
|
+
Some(TypeDef::Struct(structure)) => {
|
|
1217
|
+
if let Some(base) = &structure.base_type {
|
|
1218
|
+
deps.push(base.clone());
|
|
1219
|
+
}
|
|
1220
|
+
for field in &structure.fields {
|
|
1221
|
+
if !field.is_cycle_cut
|
|
1222
|
+
&& !(field.cardinality.is_list()
|
|
1223
|
+
&& matches!(&field.type_ref, TypeRef::Named(target) if matches!(ir.types.get(target), Some(TypeDef::Struct(_)))))
|
|
1224
|
+
{
|
|
1225
|
+
self.collect_type_dependencies(&field.type_ref, ir, current, &mut deps);
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
Some(TypeDef::Union(union)) => {
|
|
1230
|
+
for branch in &union.branches {
|
|
1231
|
+
if !matches!(&branch.type_ref, TypeRef::Boxed(inner) if matches!(inner.as_ref(), TypeRef::Named(target) if matches!(ir.types.get(target), Some(TypeDef::Struct(_)))))
|
|
1232
|
+
{
|
|
1233
|
+
self.collect_type_dependencies(&branch.type_ref, ir, current, &mut deps);
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
Some(TypeDef::Simple(simple)) => {
|
|
1238
|
+
self.collect_type_dependencies(&simple.base_type, ir, current, &mut deps);
|
|
1239
|
+
}
|
|
1240
|
+
_ => {}
|
|
1241
|
+
}
|
|
1242
|
+
deps.iter()
|
|
1243
|
+
.any(|dep| self.has_cpp_dependency_path(ir, dep, goal, seen))
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
/// Put definitions before every value use that requires a complete type.
|
|
1130
1247
|
fn topological_sort_types(&self, ir: &SchemaIR) -> Vec<QName> {
|
|
1131
1248
|
let mut in_degree: HashMap<QName, usize> = HashMap::new();
|
|
1132
1249
|
let mut adj: HashMap<QName, Vec<QName>> = HashMap::new();
|
|
@@ -1147,14 +1264,20 @@ concept XmlModel = requires(T a) {{
|
|
|
1147
1264
|
}
|
|
1148
1265
|
for f in &s.fields {
|
|
1149
1266
|
// Skip cycle cuts because they use std::unique_ptr (only need forward declarations)
|
|
1150
|
-
if !f.is_cycle_cut
|
|
1267
|
+
if !f.is_cycle_cut
|
|
1268
|
+
&& !(f.cardinality.is_list()
|
|
1269
|
+
&& matches!(&f.type_ref, TypeRef::Named(target) if matches!(ir.types.get(target), Some(TypeDef::Struct(_)))))
|
|
1270
|
+
{
|
|
1151
1271
|
self.collect_type_dependencies(&f.type_ref, ir, qname, &mut deps);
|
|
1152
1272
|
}
|
|
1153
1273
|
}
|
|
1154
1274
|
}
|
|
1155
1275
|
TypeDef::Union(u) => {
|
|
1156
1276
|
for b in &u.branches {
|
|
1157
|
-
|
|
1277
|
+
if !matches!(&b.type_ref, TypeRef::Boxed(inner) if matches!(inner.as_ref(), TypeRef::Named(target) if matches!(ir.types.get(target), Some(TypeDef::Struct(_)))))
|
|
1278
|
+
{
|
|
1279
|
+
self.collect_type_dependencies(&b.type_ref, ir, qname, &mut deps);
|
|
1280
|
+
}
|
|
1158
1281
|
}
|
|
1159
1282
|
}
|
|
1160
1283
|
TypeDef::Simple(s) => {
|
|
@@ -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
|
-
|
|
383
|
-
|
|
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
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
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
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
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
|
|
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(¤t) 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>
|
|
@@ -33,6 +33,64 @@ fn simple_aliases_follow_their_base_types() {
|
|
|
33
33
|
);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
#[test]
|
|
37
|
+
fn mixed_content_mutual_cycle_compiles() {
|
|
38
|
+
use polyxml::schema_parser::XsdParser;
|
|
39
|
+
let schema = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:test" xmlns:t="urn:test">
|
|
40
|
+
<xs:complexType name="Cd"><xs:sequence><xs:element name="originalText" type="t:Ed" minOccurs="0"/></xs:sequence></xs:complexType>
|
|
41
|
+
<xs:complexType name="Ed" mixed="true"><xs:sequence><xs:element name="concept" type="t:Cd" minOccurs="0"/></xs:sequence></xs:complexType>
|
|
42
|
+
</xs:schema>"#;
|
|
43
|
+
let ir = XsdParser::new().parse_str(schema).unwrap();
|
|
44
|
+
let code = CppCodegen::new(CppOptions::default()).generate_header(&ir);
|
|
45
|
+
let dir = tempdir().unwrap();
|
|
46
|
+
let header = dir.path().join("model.hpp");
|
|
47
|
+
fs::write(&header, code).unwrap();
|
|
48
|
+
fs::write(
|
|
49
|
+
dir.path().join("main.cpp"),
|
|
50
|
+
"#include \"model.hpp\"\nint main() { return 0; }\n",
|
|
51
|
+
)
|
|
52
|
+
.unwrap();
|
|
53
|
+
let result = Command::new("g++")
|
|
54
|
+
.args(["-std=c++20", "-fsyntax-only", "-fmax-errors=3"])
|
|
55
|
+
.arg(dir.path().join("main.cpp"))
|
|
56
|
+
.output()
|
|
57
|
+
.unwrap();
|
|
58
|
+
assert!(
|
|
59
|
+
result.status.success(),
|
|
60
|
+
"{}",
|
|
61
|
+
String::from_utf8_lossy(&result.stderr)
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
#[test]
|
|
66
|
+
fn mixed_content_self_cycle_compiles() {
|
|
67
|
+
use polyxml::schema_parser::XsdParser;
|
|
68
|
+
let schema = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:test" xmlns:t="urn:test">
|
|
69
|
+
<xs:complexType name="Thumbnail" mixed="true"><xs:sequence>
|
|
70
|
+
<xs:element name="thumbnail" type="t:Thumbnail" minOccurs="0" maxOccurs="unbounded"/>
|
|
71
|
+
</xs:sequence></xs:complexType>
|
|
72
|
+
</xs:schema>"#;
|
|
73
|
+
let ir = XsdParser::new().parse_str(schema).unwrap();
|
|
74
|
+
let code = CppCodegen::new(CppOptions::default()).generate_header(&ir);
|
|
75
|
+
let dir = tempdir().unwrap();
|
|
76
|
+
fs::write(dir.path().join("model.hpp"), code).unwrap();
|
|
77
|
+
fs::write(
|
|
78
|
+
dir.path().join("main.cpp"),
|
|
79
|
+
"#include \"model.hpp\"\nint main() { return 0; }\n",
|
|
80
|
+
)
|
|
81
|
+
.unwrap();
|
|
82
|
+
let result = Command::new("g++")
|
|
83
|
+
.args(["-std=c++20", "-fsyntax-only", "-fmax-errors=3"])
|
|
84
|
+
.arg(dir.path().join("main.cpp"))
|
|
85
|
+
.output()
|
|
86
|
+
.unwrap();
|
|
87
|
+
assert!(
|
|
88
|
+
result.status.success(),
|
|
89
|
+
"{}",
|
|
90
|
+
String::from_utf8_lossy(&result.stderr)
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
36
94
|
#[test]
|
|
37
95
|
fn test_cpp_sanitization() {
|
|
38
96
|
assert_eq!(to_cpp_field_name("class"), "class_");
|
|
@@ -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() {
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{polyxml-0.30.3 → polyxml-0.30.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
|