polyxml 0.33.7__tar.gz → 0.33.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {polyxml-0.33.7 → polyxml-0.33.8}/Cargo.lock +6 -6
- {polyxml-0.33.7 → polyxml-0.33.8}/Cargo.toml +1 -1
- {polyxml-0.33.7 → polyxml-0.33.8}/PKG-INFO +1 -1
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/src/codegen/csharp/mod.rs +40 -10
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/src/codegen/python/mod.rs +3 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/src/parser.rs +10 -3
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/src/schema.rs +46 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/src/serializer.rs +6 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/tests/test_csharp_codegen.rs +44 -0
- polyxml-0.33.8/crates/polyxml-core/tests/test_value_constraints.rs +30 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-python/src/lib.rs +9 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-python/tests/test_generated_models.py +36 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/pyproject.toml +1 -1
- {polyxml-0.33.7 → polyxml-0.33.8}/README.md +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/Cargo.toml +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/README.md +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/benches/core_benchmarks.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/benches/support/tag_dispatch_fixtures.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/benches/tag_dispatch.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/src/codegen/cpp/mod.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/src/codegen/go/mod.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/src/codegen/java/codec.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/src/codegen/java/mod.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/src/codegen/java/models.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/src/codegen/mod.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/src/codegen/python/aot.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/src/codegen/rust/mod.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/src/codegen/typescript/mod.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/src/converters.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/src/error.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/src/ir/chunker.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/src/ir/mod.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/src/ir/tarjan.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/src/json.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/src/lib.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/src/schema_parser/mod.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/src/transcoder.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/src/value.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/tests/fixtures/lexical_union.xsd +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/tests/fixtures/mixed_content.xsd +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/tests/fixtures/ts_mixed_content_inheritance.xsd +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/tests/test_adversarial_identifiers.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/tests/test_any_attribute.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/tests/test_any_element.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/tests/test_conformance.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/tests/test_core.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/tests/test_cpp_codegen.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/tests/test_go_codegen.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/tests/test_java_codegen.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/tests/test_json.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/tests/test_lexical_union_codegen.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/tests/test_mixed_content.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/tests/test_pattern_codegen.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/tests/test_python_codegen.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/tests/test_qualified_attributes.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/tests/test_rust_codecs.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/tests/test_rust_codegen.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/tests/test_schema_audit.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/tests/test_schema_ir.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/tests/test_security.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/tests/test_transcoder.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/tests/test_ts_codegen.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/tests/test_unbounded_choice.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-core/tests/test_xsi_type.rs +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-python/Cargo.toml +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-python/README.md +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-python/benches/aot_vs_dataclass.py +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-python/tests/test_conformance.py +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-python/tests/test_json.py +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-python/tests/test_mixed_content.py +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-python/tests/test_polyxml.py +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/crates/polyxml-python/tests/test_xsi_type.py +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/python/polyxml/AGENT_GUIDE.md +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/python/polyxml/__init__.py +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/python/polyxml/compat/__init__.py +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/python/polyxml/compat/xsdata.py +0 -0
- {polyxml-0.33.7 → polyxml-0.33.8}/python/polyxml/py.typed +0 -0
|
@@ -608,7 +608,7 @@ dependencies = [
|
|
|
608
608
|
|
|
609
609
|
[[package]]
|
|
610
610
|
name = "polyxml"
|
|
611
|
-
version = "0.33.
|
|
611
|
+
version = "0.33.8"
|
|
612
612
|
dependencies = [
|
|
613
613
|
"criterion",
|
|
614
614
|
"heck",
|
|
@@ -629,14 +629,14 @@ dependencies = [
|
|
|
629
629
|
|
|
630
630
|
[[package]]
|
|
631
631
|
name = "polyxml-c"
|
|
632
|
-
version = "0.33.
|
|
632
|
+
version = "0.33.8"
|
|
633
633
|
dependencies = [
|
|
634
634
|
"polyxml",
|
|
635
635
|
]
|
|
636
636
|
|
|
637
637
|
[[package]]
|
|
638
638
|
name = "polyxml-cli"
|
|
639
|
-
version = "0.33.
|
|
639
|
+
version = "0.33.8"
|
|
640
640
|
dependencies = [
|
|
641
641
|
"clap",
|
|
642
642
|
"glob",
|
|
@@ -650,7 +650,7 @@ dependencies = [
|
|
|
650
650
|
|
|
651
651
|
[[package]]
|
|
652
652
|
name = "polyxml-js"
|
|
653
|
-
version = "0.33.
|
|
653
|
+
version = "0.33.8"
|
|
654
654
|
dependencies = [
|
|
655
655
|
"napi",
|
|
656
656
|
"napi-build",
|
|
@@ -660,7 +660,7 @@ dependencies = [
|
|
|
660
660
|
|
|
661
661
|
[[package]]
|
|
662
662
|
name = "polyxml-python"
|
|
663
|
-
version = "0.33.
|
|
663
|
+
version = "0.33.8"
|
|
664
664
|
dependencies = [
|
|
665
665
|
"lexical-core",
|
|
666
666
|
"polyxml",
|
|
@@ -670,7 +670,7 @@ dependencies = [
|
|
|
670
670
|
|
|
671
671
|
[[package]]
|
|
672
672
|
name = "polyxml-wasm"
|
|
673
|
-
version = "0.33.
|
|
673
|
+
version = "0.33.8"
|
|
674
674
|
dependencies = [
|
|
675
675
|
"polyxml",
|
|
676
676
|
"quick-xml",
|
|
@@ -877,11 +877,11 @@ impl CSharpCodegen {
|
|
|
877
877
|
ir: &SchemaIR,
|
|
878
878
|
indent: &str,
|
|
879
879
|
) -> bool {
|
|
880
|
-
let Some(default) = field.default_value.as_ref() else {
|
|
880
|
+
let Some(default) = field.default_value.as_ref().or(field.fixed_value.as_ref()) else {
|
|
881
881
|
return false;
|
|
882
882
|
};
|
|
883
883
|
if !self.options.emit_xml_attributes
|
|
884
|
-
|| field.kind
|
|
884
|
+
|| !matches!(field.kind, FieldKind::Element | FieldKind::Attribute)
|
|
885
885
|
|| field.cardinality.is_list()
|
|
886
886
|
|| matches!(field.type_ref, TypeRef::Named(ref q) if matches!(ir.types.get(q), Some(TypeDef::Struct(_) | TypeDef::Union(_))))
|
|
887
887
|
{
|
|
@@ -894,19 +894,49 @@ impl CSharpCodegen {
|
|
|
894
894
|
.as_ref()
|
|
895
895
|
.map(|ns| format!(", Namespace = {ns:?}"))
|
|
896
896
|
.unwrap_or_default();
|
|
897
|
+
let parse = |text: &str| {
|
|
898
|
+
format!("({base})new XmlSerializer(typeof({base}), new XmlRootAttribute(\"value\")).Deserialize(new System.IO.StringReader(\"<value>\" + System.Security.SecurityElement.Escape({text}) + \"</value>\"))!")
|
|
899
|
+
};
|
|
900
|
+
if let Some(fixed) = &field.fixed_value {
|
|
901
|
+
let expected = parse(&format!("{fixed:?}"));
|
|
902
|
+
let initial = if field.cardinality.is_optional() && field.kind == FieldKind::Element {
|
|
903
|
+
"default!".to_string()
|
|
904
|
+
} else {
|
|
905
|
+
expected.clone()
|
|
906
|
+
};
|
|
907
|
+
writeln!(out, "{indent} private {ty} _{name} = {initial};
|
|
908
|
+
{indent} [XmlIgnore] public {ty} {name} {{ get => _{name}; set {{ if (value is not null && !object.Equals(value, {expected})) throw new System.ComponentModel.DataAnnotations.ValidationException(\"Fixed value constraint violated for {name}\"); _{name} = value; }} }}").unwrap();
|
|
909
|
+
} else {
|
|
910
|
+
let initial = if field.kind == FieldKind::Attribute {
|
|
911
|
+
parse(&format!("{default:?}"))
|
|
912
|
+
} else {
|
|
913
|
+
"default!".to_string()
|
|
914
|
+
};
|
|
915
|
+
writeln!(
|
|
916
|
+
out,
|
|
917
|
+
"{indent} [XmlIgnore] public {ty} {name} {{ get; set; }} = {initial};"
|
|
918
|
+
)
|
|
919
|
+
.unwrap();
|
|
920
|
+
}
|
|
921
|
+
let kind = if field.kind == FieldKind::Attribute {
|
|
922
|
+
"XmlAttribute"
|
|
923
|
+
} else {
|
|
924
|
+
"XmlElement"
|
|
925
|
+
};
|
|
897
926
|
writeln!(
|
|
898
927
|
out,
|
|
899
|
-
"{indent} [
|
|
900
|
-
)
|
|
901
|
-
.unwrap();
|
|
902
|
-
writeln!(
|
|
903
|
-
out,
|
|
904
|
-
"{indent} [XmlElement({:?}{namespace})] public string? {name}Xml {{",
|
|
928
|
+
"{indent} [{kind}({:?}{namespace})] public string? {name}Xml {{",
|
|
905
929
|
field.xml_name
|
|
906
930
|
)
|
|
907
931
|
.unwrap();
|
|
908
932
|
writeln!(out, "{indent} get {{ if ({name} is null) return null; 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();
|
|
909
|
-
|
|
933
|
+
let text = if field.kind == FieldKind::Attribute {
|
|
934
|
+
"value".into()
|
|
935
|
+
} else {
|
|
936
|
+
format!("value.Length == 0 ? {default:?} : value")
|
|
937
|
+
};
|
|
938
|
+
let parsed = parse("text");
|
|
939
|
+
writeln!(out, "{indent} set {{ if (value is null) {{ {name} = default!; return; }} var text = {text}; {name} = {parsed}; }}
|
|
910
940
|
{indent} }}").unwrap();
|
|
911
941
|
if field.cardinality.is_optional() || field.nillable {
|
|
912
942
|
writeln!(
|
|
@@ -1079,7 +1109,7 @@ impl CSharpCodegen {
|
|
|
1079
1109
|
}
|
|
1080
1110
|
|
|
1081
1111
|
let has_lexical_union = self.options.emit_xml_attributes && s.fields.iter().any(|f| {
|
|
1082
|
-
f.default_value.is_some() || matches!(&f.type_ref, TypeRef::Named(q) if matches!(ir.types.get(q), Some(TypeDef::Union(u)) if u.is_lexical()))
|
|
1112
|
+
f.default_value.is_some() || f.fixed_value.is_some() || matches!(&f.type_ref, TypeRef::Named(q) if matches!(ir.types.get(q), Some(TypeDef::Union(u)) if u.is_lexical()))
|
|
1083
1113
|
});
|
|
1084
1114
|
|
|
1085
1115
|
if self.options.use_records && has_lexical_union {
|
|
@@ -993,6 +993,9 @@ impl PythonCodegen {
|
|
|
993
993
|
parts.push(format!("\"namespace\": \"{}\"", ns));
|
|
994
994
|
}
|
|
995
995
|
|
|
996
|
+
if let Some(ref fixed) = field.fixed_value {
|
|
997
|
+
parts.push(format!("\"fixed\": {fixed:?}"));
|
|
998
|
+
}
|
|
996
999
|
if let Some(ref default) = field.default_value {
|
|
997
1000
|
parts.push(format!("\"default\": {default:?}"));
|
|
998
1001
|
}
|
|
@@ -73,9 +73,10 @@ impl StackFrame {
|
|
|
73
73
|
fn finish(mut self) -> Result<PolyValue> {
|
|
74
74
|
for (index, field) in self.schema.fields.iter().enumerate() {
|
|
75
75
|
if field.kind == FieldKind::Attribute && self.values[index].is_none() {
|
|
76
|
-
if let (Some(default), ValueType::Scalar(scalar)) =
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
if let (Some(default), ValueType::Scalar(scalar)) = (
|
|
77
|
+
&field.default_value.as_ref().or(field.fixed_value.as_ref()),
|
|
78
|
+
&field.val_type,
|
|
79
|
+
) {
|
|
79
80
|
self.values[index] = Some(ValueConverter::parse_scalar(
|
|
80
81
|
scalar,
|
|
81
82
|
default.as_bytes(),
|
|
@@ -113,6 +114,11 @@ impl StackFrame {
|
|
|
113
114
|
}
|
|
114
115
|
}
|
|
115
116
|
|
|
117
|
+
for (index, field) in self.schema.fields.iter().enumerate() {
|
|
118
|
+
if let Some(value) = self.values[index].as_ref() {
|
|
119
|
+
crate::schema::validate_fixed(field, value)?;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
116
122
|
Ok(PolyValue::Record {
|
|
117
123
|
schema: self.schema,
|
|
118
124
|
values: self.values.into_vec().into_boxed_slice(),
|
|
@@ -129,6 +135,7 @@ fn parse_field_scalar(
|
|
|
129
135
|
field
|
|
130
136
|
.default_value
|
|
131
137
|
.as_ref()
|
|
138
|
+
.or(field.fixed_value.as_ref())
|
|
132
139
|
.map_or(text, |value| value.as_bytes())
|
|
133
140
|
} else {
|
|
134
141
|
text
|
|
@@ -66,6 +66,7 @@ pub struct FieldSchema {
|
|
|
66
66
|
pub val_type: ValueType,
|
|
67
67
|
pub required: bool,
|
|
68
68
|
pub default_value: Option<String>,
|
|
69
|
+
pub fixed_value: Option<String>,
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
impl FieldSchema {
|
|
@@ -83,6 +84,7 @@ impl FieldSchema {
|
|
|
83
84
|
val_type,
|
|
84
85
|
required: false,
|
|
85
86
|
default_value: None,
|
|
87
|
+
fixed_value: None,
|
|
86
88
|
}
|
|
87
89
|
}
|
|
88
90
|
|
|
@@ -354,6 +356,7 @@ impl ModelSchema {
|
|
|
354
356
|
|
|
355
357
|
let mut field_schema = FieldSchema::new(&f.name, f.xml_name.as_bytes(), kind, val_type);
|
|
356
358
|
field_schema.default_value = f.default_value.clone();
|
|
359
|
+
field_schema.fixed_value = f.fixed_value.clone();
|
|
357
360
|
if let Some(ref ns) = f.namespace {
|
|
358
361
|
field_schema = field_schema.namespace(ns);
|
|
359
362
|
}
|
|
@@ -633,3 +636,46 @@ impl ModelSchemaBuilder {
|
|
|
633
636
|
})
|
|
634
637
|
}
|
|
635
638
|
}
|
|
639
|
+
|
|
640
|
+
/// Validate fixed constraints in value space rather than comparing lexical spellings.
|
|
641
|
+
pub(crate) fn validate_fixed(
|
|
642
|
+
field: &FieldSchema,
|
|
643
|
+
value: &crate::value::PolyValue,
|
|
644
|
+
) -> crate::error::Result<()> {
|
|
645
|
+
use crate::value::PolyValue;
|
|
646
|
+
let Some(fixed) = &field.fixed_value else {
|
|
647
|
+
return Ok(());
|
|
648
|
+
};
|
|
649
|
+
fn check(
|
|
650
|
+
ty: &ValueType,
|
|
651
|
+
value: &PolyValue,
|
|
652
|
+
fixed: &str,
|
|
653
|
+
name: &str,
|
|
654
|
+
) -> crate::error::Result<()> {
|
|
655
|
+
if matches!(value, PolyValue::Null) {
|
|
656
|
+
return Ok(());
|
|
657
|
+
}
|
|
658
|
+
match (ty, value) {
|
|
659
|
+
(ValueType::Scalar(scalar), value) => {
|
|
660
|
+
let expected = crate::converters::ValueConverter::parse_scalar(
|
|
661
|
+
scalar,
|
|
662
|
+
fixed.as_bytes(),
|
|
663
|
+
name,
|
|
664
|
+
)?;
|
|
665
|
+
if *value != expected {
|
|
666
|
+
return Err(crate::error::PolyXmlError::SchemaError(format!(
|
|
667
|
+
"Fixed value constraint violated for {name}"
|
|
668
|
+
)));
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
(ValueType::List(inner), PolyValue::List(items)) => {
|
|
672
|
+
for item in items {
|
|
673
|
+
check(inner, item, fixed, name)?;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
_ => {}
|
|
677
|
+
}
|
|
678
|
+
Ok(())
|
|
679
|
+
}
|
|
680
|
+
check(&field.val_type, value, fixed, &field.name)
|
|
681
|
+
}
|
|
@@ -296,6 +296,12 @@ impl XmlSerializer {
|
|
|
296
296
|
}
|
|
297
297
|
};
|
|
298
298
|
|
|
299
|
+
for (index, field) in schema.fields.iter().enumerate() {
|
|
300
|
+
if let Some(value) = get_field(index, &field.name) {
|
|
301
|
+
crate::schema::validate_fixed(field, value)?;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
299
305
|
let local_tag = std::str::from_utf8(tag_name)?;
|
|
300
306
|
let qualified_tag = if let Some(ctx) = ns_ctx {
|
|
301
307
|
ctx.qualify_element(local_tag, element_ns)
|
|
@@ -1278,3 +1278,47 @@ if(present&&!writer.ToString().Contains("<Flag>false</Flag>"))throw new Exceptio
|
|
|
1278
1278
|
);
|
|
1279
1279
|
}
|
|
1280
1280
|
}
|
|
1281
|
+
|
|
1282
|
+
#[test]
|
|
1283
|
+
fn fixed_values_are_enforced_on_read_and_write() {
|
|
1284
|
+
let ir = polyxml::schema_parser::XsdParser::new()
|
|
1285
|
+
.parse_str(include_str!("../../../research/fixtures/fixed_values.xsd"))
|
|
1286
|
+
.unwrap();
|
|
1287
|
+
for use_records in [true, false] {
|
|
1288
|
+
let temp = tempdir().unwrap();
|
|
1289
|
+
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></PropertyGroup></Project>"#).unwrap();
|
|
1290
|
+
fs::write(
|
|
1291
|
+
temp.path().join("Models.cs"),
|
|
1292
|
+
CSharpCodegen::new(CSharpOptions {
|
|
1293
|
+
namespace: "Models".into(),
|
|
1294
|
+
use_records,
|
|
1295
|
+
..Default::default()
|
|
1296
|
+
})
|
|
1297
|
+
.generate_module(&ir),
|
|
1298
|
+
)
|
|
1299
|
+
.unwrap();
|
|
1300
|
+
fs::write(temp.path().join("Program.cs"),r#"using Models;using System.Xml.Serialization;using System.ComponentModel.DataAnnotations;
|
|
1301
|
+
var serializer=new XmlSerializer(typeof(Root));
|
|
1302
|
+
foreach(var xml in new[]{"<Root><Code>0013</Code><Flag>1</Flag></Root>","<Root><Code/><Flag/></Root>"}){
|
|
1303
|
+
var root=(Root)serializer.Deserialize(new StringReader(xml))!;
|
|
1304
|
+
if(root.Code!="0013"||root.Flag!=true||root.Mode!="auto")throw new Exception("fixed defaults missing");
|
|
1305
|
+
var writer=new StringWriter();serializer.Serialize(writer,root);
|
|
1306
|
+
try{root.Code="wrong";throw new Exception("mutation accepted");}catch(ValidationException){}
|
|
1307
|
+
}
|
|
1308
|
+
foreach(var xml in new[]{"<Root><Code>wrong</Code></Root>","<Root mode='wrong'><Code>0013</Code></Root>","<Root><Code>0013</Code><Flag>false</Flag></Root>"}){
|
|
1309
|
+
try{serializer.Deserialize(new StringReader(xml));throw new Exception("invalid input accepted");}catch(InvalidOperationException){}
|
|
1310
|
+
}"#).unwrap();
|
|
1311
|
+
let _lock = DOTNET_LOCK.lock().unwrap();
|
|
1312
|
+
let result = dotnet_command()
|
|
1313
|
+
.arg("run")
|
|
1314
|
+
.current_dir(temp.path())
|
|
1315
|
+
.output()
|
|
1316
|
+
.unwrap();
|
|
1317
|
+
assert!(
|
|
1318
|
+
result.status.success(),
|
|
1319
|
+
"{}{}",
|
|
1320
|
+
String::from_utf8_lossy(&result.stdout),
|
|
1321
|
+
String::from_utf8_lossy(&result.stderr)
|
|
1322
|
+
);
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
use polyxml::schema::ModelSchema;
|
|
2
|
+
use polyxml::schema_parser::XsdParser;
|
|
3
|
+
use polyxml::{deserialize, serialize};
|
|
4
|
+
use std::sync::Arc;
|
|
5
|
+
|
|
6
|
+
#[test]
|
|
7
|
+
fn fixed_constraints_cover_input_and_output_in_value_space() {
|
|
8
|
+
let ir = XsdParser::new()
|
|
9
|
+
.parse_str(include_str!("../../../research/fixtures/fixed_values.xsd"))
|
|
10
|
+
.unwrap();
|
|
11
|
+
let schema = ModelSchema::from_ir(&ir, Some("Root")).unwrap();
|
|
12
|
+
for xml in [
|
|
13
|
+
"<Root><Code>0013</Code><Flag>1</Flag></Root>",
|
|
14
|
+
"<Root><Code/><Flag/></Root>",
|
|
15
|
+
] {
|
|
16
|
+
let value = deserialize(xml.as_bytes(), Arc::clone(&schema)).unwrap();
|
|
17
|
+
let output = serialize("Root", &value, &schema, None).unwrap();
|
|
18
|
+
deserialize(&output, Arc::clone(&schema)).unwrap();
|
|
19
|
+
}
|
|
20
|
+
for xml in [
|
|
21
|
+
"<Root><Code>wrong</Code></Root>",
|
|
22
|
+
"<Root mode='wrong'><Code>0013</Code></Root>",
|
|
23
|
+
"<Root><Code>0013</Code><Flag>false</Flag></Root>",
|
|
24
|
+
] {
|
|
25
|
+
assert!(deserialize(xml.as_bytes(), Arc::clone(&schema))
|
|
26
|
+
.unwrap_err()
|
|
27
|
+
.to_string()
|
|
28
|
+
.contains("Fixed value constraint"));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -417,6 +417,10 @@ fn extract_schema_from_class<'py>(
|
|
|
417
417
|
});
|
|
418
418
|
|
|
419
419
|
let mut field_schema = FieldSchema::new(py_name, xml_name.as_bytes(), kind, val_type);
|
|
420
|
+
field_schema.fixed_value = meta
|
|
421
|
+
.as_ref()
|
|
422
|
+
.and_then(|m| m.get_item("fixed").ok())
|
|
423
|
+
.and_then(|v| v.extract::<String>().ok());
|
|
420
424
|
field_schema.default_value = meta
|
|
421
425
|
.as_ref()
|
|
422
426
|
.and_then(|m| m.get_item("default").ok())
|
|
@@ -498,6 +502,11 @@ fn extract_schema_from_class<'py>(
|
|
|
498
502
|
});
|
|
499
503
|
|
|
500
504
|
let mut field_schema = FieldSchema::new(py_name, xml_name.as_bytes(), kind, val_type);
|
|
505
|
+
field_schema.fixed_value = field_obj
|
|
506
|
+
.getattr("metadata")
|
|
507
|
+
.ok()
|
|
508
|
+
.and_then(|m| m.get_item("fixed").ok())
|
|
509
|
+
.and_then(|v| v.extract::<String>().ok());
|
|
501
510
|
field_schema.default_value = field_obj
|
|
502
511
|
.getattr("metadata")
|
|
503
512
|
.ok()
|
|
@@ -598,3 +598,39 @@ def test_element_defaults_preserve_absence(tmp_path, backend):
|
|
|
598
598
|
encoded = present.to_xml()
|
|
599
599
|
assert ET.fromstring(encoded).findtext("Flag") == "false"
|
|
600
600
|
assert module.Root.from_xml(encoded).count == 42
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
@pytest.mark.parametrize("backend", ["dataclass", "pydantic"])
|
|
604
|
+
def test_fixed_values_validate_input_and_output(tmp_path, backend):
|
|
605
|
+
schema = pathlib.Path(__file__).resolve().parents[3] / "research/fixtures/fixed_values.xsd"
|
|
606
|
+
subprocess.run(
|
|
607
|
+
[
|
|
608
|
+
str(_get_polyxml_bin()),
|
|
609
|
+
"generate",
|
|
610
|
+
str(schema),
|
|
611
|
+
"--lang",
|
|
612
|
+
"python",
|
|
613
|
+
"--backend",
|
|
614
|
+
backend,
|
|
615
|
+
"--out",
|
|
616
|
+
str(tmp_path),
|
|
617
|
+
],
|
|
618
|
+
check=True,
|
|
619
|
+
capture_output=True,
|
|
620
|
+
)
|
|
621
|
+
module = _load_module_from_file(f"fixed_{backend}", tmp_path / "fixed_values.py")
|
|
622
|
+
for document in ["<Root><Code>0013</Code><Flag>1</Flag></Root>", "<Root><Code/><Flag/></Root>"]:
|
|
623
|
+
root = module.Root.from_xml(document)
|
|
624
|
+
assert (root.code, root.flag, root.mode) == ("0013", True, "auto")
|
|
625
|
+
assert module.Root.from_xml(root.to_xml()).code == "0013"
|
|
626
|
+
for document in [
|
|
627
|
+
"<Root><Code>wrong</Code></Root>",
|
|
628
|
+
"<Root mode='wrong'><Code>0013</Code></Root>",
|
|
629
|
+
"<Root><Code>0013</Code><Flag>false</Flag></Root>",
|
|
630
|
+
]:
|
|
631
|
+
with pytest.raises(ValueError, match="Fixed value constraint"):
|
|
632
|
+
module.Root.from_xml(document)
|
|
633
|
+
root = module.Root.from_xml("<Root><Code>0013</Code></Root>")
|
|
634
|
+
root.code = "wrong"
|
|
635
|
+
with pytest.raises(ValueError, match="Fixed value constraint"):
|
|
636
|
+
root.to_xml()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{polyxml-0.33.7 → polyxml-0.33.8}/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
|