polyxml 0.26.0__tar.gz → 0.27.0__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.26.0 → polyxml-0.27.0}/Cargo.lock +6 -6
- {polyxml-0.26.0 → polyxml-0.27.0}/Cargo.toml +1 -1
- {polyxml-0.26.0 → polyxml-0.27.0}/PKG-INFO +1 -1
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/src/codegen/java/mod.rs +10 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/src/codegen/typescript/mod.rs +15 -0
- polyxml-0.27.0/crates/polyxml-core/src/converters.rs +178 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/src/schema.rs +6 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/src/schema_parser/mod.rs +75 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/src/serializer.rs +16 -1
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/tests/test_core.rs +49 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/tests/test_pattern_codegen.rs +53 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/pyproject.toml +1 -1
- polyxml-0.26.0/crates/polyxml-core/src/converters.rs +0 -80
- {polyxml-0.26.0 → polyxml-0.27.0}/README.md +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/Cargo.toml +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/README.md +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/benches/core_benchmarks.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/benches/support/tag_dispatch_fixtures.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/benches/tag_dispatch.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/src/codegen/cpp/mod.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/src/codegen/csharp/mod.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/src/codegen/go/mod.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/src/codegen/java/codec.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/src/codegen/java/models.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/src/codegen/mod.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/src/codegen/python/aot.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/src/codegen/python/mod.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/src/codegen/rust/mod.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/src/error.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/src/ir/mod.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/src/ir/tarjan.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/src/json.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/src/lib.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/src/parser.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/src/transcoder.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/src/value.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/tests/test_adversarial_identifiers.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/tests/test_conformance.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/tests/test_cpp_codegen.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/tests/test_csharp_codegen.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/tests/test_go_codegen.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/tests/test_java_codegen.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/tests/test_json.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/tests/test_python_codegen.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/tests/test_rust_codecs.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/tests/test_rust_codegen.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/tests/test_schema_audit.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/tests/test_schema_ir.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/tests/test_security.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/tests/test_transcoder.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/tests/test_ts_codegen.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/tests/test_unbounded_choice.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-core/tests/test_xsi_type.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-python/Cargo.toml +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-python/README.md +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-python/benches/aot_vs_dataclass.py +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-python/src/lib.rs +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-python/tests/test_conformance.py +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-python/tests/test_generated_models.py +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-python/tests/test_json.py +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-python/tests/test_polyxml.py +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/crates/polyxml-python/tests/test_xsi_type.py +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/python/polyxml/AGENT_GUIDE.md +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/python/polyxml/__init__.py +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/python/polyxml/compat/__init__.py +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/python/polyxml/compat/xsdata.py +0 -0
- {polyxml-0.26.0 → polyxml-0.27.0}/python/polyxml/py.typed +0 -0
|
@@ -608,7 +608,7 @@ dependencies = [
|
|
|
608
608
|
|
|
609
609
|
[[package]]
|
|
610
610
|
name = "polyxml"
|
|
611
|
-
version = "0.
|
|
611
|
+
version = "0.27.0"
|
|
612
612
|
dependencies = [
|
|
613
613
|
"criterion",
|
|
614
614
|
"heck",
|
|
@@ -628,14 +628,14 @@ dependencies = [
|
|
|
628
628
|
|
|
629
629
|
[[package]]
|
|
630
630
|
name = "polyxml-c"
|
|
631
|
-
version = "0.
|
|
631
|
+
version = "0.27.0"
|
|
632
632
|
dependencies = [
|
|
633
633
|
"polyxml",
|
|
634
634
|
]
|
|
635
635
|
|
|
636
636
|
[[package]]
|
|
637
637
|
name = "polyxml-cli"
|
|
638
|
-
version = "0.
|
|
638
|
+
version = "0.27.0"
|
|
639
639
|
dependencies = [
|
|
640
640
|
"clap",
|
|
641
641
|
"glob",
|
|
@@ -649,7 +649,7 @@ dependencies = [
|
|
|
649
649
|
|
|
650
650
|
[[package]]
|
|
651
651
|
name = "polyxml-js"
|
|
652
|
-
version = "0.
|
|
652
|
+
version = "0.27.0"
|
|
653
653
|
dependencies = [
|
|
654
654
|
"napi",
|
|
655
655
|
"napi-build",
|
|
@@ -659,7 +659,7 @@ dependencies = [
|
|
|
659
659
|
|
|
660
660
|
[[package]]
|
|
661
661
|
name = "polyxml-python"
|
|
662
|
-
version = "0.
|
|
662
|
+
version = "0.27.0"
|
|
663
663
|
dependencies = [
|
|
664
664
|
"lexical-core",
|
|
665
665
|
"polyxml",
|
|
@@ -669,7 +669,7 @@ dependencies = [
|
|
|
669
669
|
|
|
670
670
|
[[package]]
|
|
671
671
|
name = "polyxml-wasm"
|
|
672
|
-
version = "0.
|
|
672
|
+
version = "0.27.0"
|
|
673
673
|
dependencies = [
|
|
674
674
|
"polyxml",
|
|
675
675
|
"quick-xml",
|
|
@@ -832,6 +832,11 @@ impl JavaCodegen {
|
|
|
832
832
|
| PrimitiveType::IdRefs
|
|
833
833
|
| PrimitiveType::Entity
|
|
834
834
|
| PrimitiveType::Entities
|
|
835
|
+
| PrimitiveType::GDay
|
|
836
|
+
| PrimitiveType::GMonth
|
|
837
|
+
| PrimitiveType::GYear
|
|
838
|
+
| PrimitiveType::GYearMonth
|
|
839
|
+
| PrimitiveType::GMonthDay
|
|
835
840
|
)
|
|
836
841
|
);
|
|
837
842
|
let is_numeric = matches!(
|
|
@@ -939,6 +944,11 @@ impl JavaCodegen {
|
|
|
939
944
|
| PrimitiveType::IdRefs
|
|
940
945
|
| PrimitiveType::Entity
|
|
941
946
|
| PrimitiveType::Entities
|
|
947
|
+
| PrimitiveType::GDay
|
|
948
|
+
| PrimitiveType::GMonth
|
|
949
|
+
| PrimitiveType::GYear
|
|
950
|
+
| PrimitiveType::GYearMonth
|
|
951
|
+
| PrimitiveType::GMonthDay
|
|
942
952
|
),
|
|
943
953
|
_ => false,
|
|
944
954
|
};
|
|
@@ -759,6 +759,11 @@ impl TypeScriptCodegen {
|
|
|
759
759
|
| PrimitiveType::IdRefs
|
|
760
760
|
| PrimitiveType::Entity
|
|
761
761
|
| PrimitiveType::Entities
|
|
762
|
+
| PrimitiveType::GDay
|
|
763
|
+
| PrimitiveType::GMonth
|
|
764
|
+
| PrimitiveType::GYear
|
|
765
|
+
| PrimitiveType::GYearMonth
|
|
766
|
+
| PrimitiveType::GMonthDay
|
|
762
767
|
),
|
|
763
768
|
_ => false,
|
|
764
769
|
};
|
|
@@ -836,6 +841,11 @@ impl TypeScriptCodegen {
|
|
|
836
841
|
| PrimitiveType::NCName
|
|
837
842
|
| PrimitiveType::Language
|
|
838
843
|
| PrimitiveType::AnyUri
|
|
844
|
+
| PrimitiveType::GDay
|
|
845
|
+
| PrimitiveType::GMonth
|
|
846
|
+
| PrimitiveType::GYear
|
|
847
|
+
| PrimitiveType::GYearMonth
|
|
848
|
+
| PrimitiveType::GMonthDay
|
|
839
849
|
),
|
|
840
850
|
_ => false,
|
|
841
851
|
};
|
|
@@ -909,6 +919,11 @@ impl TypeScriptCodegen {
|
|
|
909
919
|
| PrimitiveType::NCName
|
|
910
920
|
| PrimitiveType::Language
|
|
911
921
|
| PrimitiveType::AnyUri
|
|
922
|
+
| PrimitiveType::GDay
|
|
923
|
+
| PrimitiveType::GMonth
|
|
924
|
+
| PrimitiveType::GYear
|
|
925
|
+
| PrimitiveType::GYearMonth
|
|
926
|
+
| PrimitiveType::GMonthDay
|
|
912
927
|
),
|
|
913
928
|
_ => false,
|
|
914
929
|
};
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
use crate::error::{PolyXmlError, Result};
|
|
2
|
+
use crate::ir::PrimitiveType;
|
|
3
|
+
use crate::schema::ScalarType;
|
|
4
|
+
use crate::value::PolyValue;
|
|
5
|
+
|
|
6
|
+
#[inline(always)]
|
|
7
|
+
pub fn trim_bytes(mut b: &[u8]) -> &[u8] {
|
|
8
|
+
while let Some((first, rest)) = b.split_first() {
|
|
9
|
+
if first.is_ascii_whitespace() {
|
|
10
|
+
b = rest;
|
|
11
|
+
} else {
|
|
12
|
+
break;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
while let Some((last, rest)) = b.split_last() {
|
|
16
|
+
if last.is_ascii_whitespace() {
|
|
17
|
+
b = rest;
|
|
18
|
+
} else {
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
b
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
pub struct ValueConverter;
|
|
26
|
+
|
|
27
|
+
impl ValueConverter {
|
|
28
|
+
pub fn parse_scalar(
|
|
29
|
+
scalar_type: &ScalarType,
|
|
30
|
+
bytes: &[u8],
|
|
31
|
+
field_name: &str,
|
|
32
|
+
) -> Result<PolyValue> {
|
|
33
|
+
match scalar_type {
|
|
34
|
+
ScalarType::String => {
|
|
35
|
+
let s = std::str::from_utf8(bytes)?;
|
|
36
|
+
Ok(PolyValue::String(s.to_string()))
|
|
37
|
+
}
|
|
38
|
+
ScalarType::Int => {
|
|
39
|
+
let trimmed = trim_bytes(bytes);
|
|
40
|
+
let val: i64 =
|
|
41
|
+
lexical_core::parse(trimmed).map_err(|_| PolyXmlError::ScalarParseError {
|
|
42
|
+
field: field_name.to_string(),
|
|
43
|
+
expected: "integer",
|
|
44
|
+
value: String::from_utf8_lossy(bytes).to_string(),
|
|
45
|
+
})?;
|
|
46
|
+
Ok(PolyValue::Int(val))
|
|
47
|
+
}
|
|
48
|
+
ScalarType::Float => {
|
|
49
|
+
let trimmed = trim_bytes(bytes);
|
|
50
|
+
let val: f64 =
|
|
51
|
+
lexical_core::parse(trimmed).map_err(|_| PolyXmlError::ScalarParseError {
|
|
52
|
+
field: field_name.to_string(),
|
|
53
|
+
expected: "float",
|
|
54
|
+
value: String::from_utf8_lossy(bytes).to_string(),
|
|
55
|
+
})?;
|
|
56
|
+
Ok(PolyValue::Float(val))
|
|
57
|
+
}
|
|
58
|
+
ScalarType::Bool => {
|
|
59
|
+
let trimmed = trim_bytes(bytes);
|
|
60
|
+
match trimmed {
|
|
61
|
+
b"true" | b"1" => Ok(PolyValue::Bool(true)),
|
|
62
|
+
b"false" | b"0" => Ok(PolyValue::Bool(false)),
|
|
63
|
+
_ => Err(PolyXmlError::ScalarParseError {
|
|
64
|
+
field: field_name.to_string(),
|
|
65
|
+
expected: "boolean ('true', 'false', '1', '0')",
|
|
66
|
+
value: String::from_utf8_lossy(bytes).to_string(),
|
|
67
|
+
}),
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
ScalarType::Decimal
|
|
71
|
+
| ScalarType::XmlDate
|
|
72
|
+
| ScalarType::XmlDateTime
|
|
73
|
+
| ScalarType::XmlTime
|
|
74
|
+
| ScalarType::XmlDuration
|
|
75
|
+
| ScalarType::Any => {
|
|
76
|
+
let s = std::str::from_utf8(trim_bytes(bytes))?;
|
|
77
|
+
Ok(PolyValue::String(s.to_string()))
|
|
78
|
+
}
|
|
79
|
+
ScalarType::XmlGregorian(kind) => {
|
|
80
|
+
let value = std::str::from_utf8(trim_bytes(bytes))?;
|
|
81
|
+
if !valid_gregorian(*kind, value.as_bytes()) {
|
|
82
|
+
return Err(PolyXmlError::ScalarParseError {
|
|
83
|
+
field: field_name.to_string(),
|
|
84
|
+
expected: "XML Schema Gregorian partial date",
|
|
85
|
+
value: value.to_string(),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
Ok(PolyValue::String(value.to_string()))
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
fn two_digits(bytes: &[u8]) -> Option<u8> {
|
|
95
|
+
if bytes.len() == 2 && bytes.iter().all(u8::is_ascii_digit) {
|
|
96
|
+
Some((bytes[0] - b'0') * 10 + bytes[1] - b'0')
|
|
97
|
+
} else {
|
|
98
|
+
None
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
fn valid_timezone(bytes: &[u8]) -> Option<&[u8]> {
|
|
103
|
+
if let Some(value) = bytes.strip_suffix(b"Z") {
|
|
104
|
+
return Some(value);
|
|
105
|
+
}
|
|
106
|
+
let split = bytes
|
|
107
|
+
.iter()
|
|
108
|
+
.rposition(|byte| *byte == b'+' || *byte == b'-')?;
|
|
109
|
+
let suffix = &bytes[split..];
|
|
110
|
+
if suffix.len() != 6 || suffix[3] != b':' {
|
|
111
|
+
return None;
|
|
112
|
+
}
|
|
113
|
+
let hours = two_digits(&suffix[1..3])?;
|
|
114
|
+
let minutes = two_digits(&suffix[4..6])?;
|
|
115
|
+
if hours > 14 || minutes > 59 || (hours == 14 && minutes != 0) {
|
|
116
|
+
return None;
|
|
117
|
+
}
|
|
118
|
+
Some(&bytes[..split])
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
fn valid_gregorian(kind: PrimitiveType, bytes: &[u8]) -> bool {
|
|
122
|
+
let has_timezone = bytes.ends_with(b"Z")
|
|
123
|
+
|| bytes.contains(&b'+')
|
|
124
|
+
|| (bytes.len() >= 6 && bytes[bytes.len() - 6] == b'-' && bytes[bytes.len() - 3] == b':');
|
|
125
|
+
let value = if has_timezone {
|
|
126
|
+
match valid_timezone(bytes) {
|
|
127
|
+
Some(value) => value,
|
|
128
|
+
None => return false,
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
131
|
+
bytes
|
|
132
|
+
};
|
|
133
|
+
match kind {
|
|
134
|
+
PrimitiveType::GDay => {
|
|
135
|
+
value.len() == 5
|
|
136
|
+
&& value.starts_with(b"---")
|
|
137
|
+
&& two_digits(&value[3..]).is_some_and(|day| (1..=31).contains(&day))
|
|
138
|
+
}
|
|
139
|
+
PrimitiveType::GMonth => {
|
|
140
|
+
value.len() == 4
|
|
141
|
+
&& value.starts_with(b"--")
|
|
142
|
+
&& two_digits(&value[2..]).is_some_and(|month| (1..=12).contains(&month))
|
|
143
|
+
}
|
|
144
|
+
PrimitiveType::GMonthDay => {
|
|
145
|
+
if value.len() != 7 || !value.starts_with(b"--") || value[4] != b'-' {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
let (Some(month), Some(day)) = (two_digits(&value[2..4]), two_digits(&value[5..7]))
|
|
149
|
+
else {
|
|
150
|
+
return false;
|
|
151
|
+
};
|
|
152
|
+
let max_day = match month {
|
|
153
|
+
2 => 29,
|
|
154
|
+
4 | 6 | 9 | 11 => 30,
|
|
155
|
+
1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
|
|
156
|
+
_ => return false,
|
|
157
|
+
};
|
|
158
|
+
day >= 1 && day <= max_day
|
|
159
|
+
}
|
|
160
|
+
PrimitiveType::GYear => valid_year(value),
|
|
161
|
+
PrimitiveType::GYearMonth => {
|
|
162
|
+
if value.len() < 7 || value[value.len() - 3] != b'-' {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
valid_year(&value[..value.len() - 3])
|
|
166
|
+
&& two_digits(&value[value.len() - 2..])
|
|
167
|
+
.is_some_and(|month| (1..=12).contains(&month))
|
|
168
|
+
}
|
|
169
|
+
_ => false,
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
fn valid_year(value: &[u8]) -> bool {
|
|
174
|
+
let digits = value.strip_prefix(b"-").unwrap_or(value);
|
|
175
|
+
digits.len() >= 4
|
|
176
|
+
&& digits.iter().all(u8::is_ascii_digit)
|
|
177
|
+
&& (digits.len() == 4 || digits[0] != b'0')
|
|
178
|
+
}
|
|
@@ -19,6 +19,7 @@ pub enum ScalarType {
|
|
|
19
19
|
XmlDateTime,
|
|
20
20
|
XmlTime,
|
|
21
21
|
XmlDuration,
|
|
22
|
+
XmlGregorian(crate::ir::PrimitiveType),
|
|
22
23
|
Any,
|
|
23
24
|
}
|
|
24
25
|
|
|
@@ -218,6 +219,11 @@ impl ModelSchema {
|
|
|
218
219
|
PrimitiveType::DateTime => ScalarType::XmlDateTime,
|
|
219
220
|
PrimitiveType::Time => ScalarType::XmlTime,
|
|
220
221
|
PrimitiveType::Date => ScalarType::XmlDate,
|
|
222
|
+
PrimitiveType::GDay
|
|
223
|
+
| PrimitiveType::GMonth
|
|
224
|
+
| PrimitiveType::GYear
|
|
225
|
+
| PrimitiveType::GYearMonth
|
|
226
|
+
| PrimitiveType::GMonthDay => ScalarType::XmlGregorian(prim),
|
|
221
227
|
PrimitiveType::Int
|
|
222
228
|
| PrimitiveType::Integer
|
|
223
229
|
| PrimitiveType::NonPositiveInteger
|
|
@@ -167,6 +167,7 @@ impl XsdParser {
|
|
|
167
167
|
// Frame post-passes: inherit pattern facets up derivation chains
|
|
168
168
|
// and expand named model group references. Both are idempotent.
|
|
169
169
|
// Cycle detection runs last so fields spliced in from groups are covered.
|
|
170
|
+
add_gregorian_types(&mut ir);
|
|
170
171
|
inherit_pattern_facets(&mut ir);
|
|
171
172
|
self.expand_group_refs(&mut ir, self.frame_depth == 0);
|
|
172
173
|
ir.resolve_cycles();
|
|
@@ -1290,6 +1291,80 @@ impl XsdParser {
|
|
|
1290
1291
|
}
|
|
1291
1292
|
}
|
|
1292
1293
|
|
|
1294
|
+
/// Give XSD Gregorian primitives named, validated types in every target.
|
|
1295
|
+
/// The original primitive remains the base of each synthesized simple type.
|
|
1296
|
+
fn add_gregorian_types(ir: &mut SchemaIR) {
|
|
1297
|
+
const NS: &str = "urn:polyxml:builtins:gregorian";
|
|
1298
|
+
const TZ: &str = r"(?:Z|[+-](?:(?:0[0-9]|1[0-3]):[0-5][0-9]|14:00))?";
|
|
1299
|
+
fn pattern(kind: PrimitiveType) -> Option<String> {
|
|
1300
|
+
let body = match kind {
|
|
1301
|
+
PrimitiveType::GDay => r"---(?:0[1-9]|[12][0-9]|3[01])",
|
|
1302
|
+
PrimitiveType::GMonth => r"--(?:0[1-9]|1[0-2])",
|
|
1303
|
+
PrimitiveType::GYear => r"-?(?:[0-9]{4}|[1-9][0-9]{4,})",
|
|
1304
|
+
PrimitiveType::GYearMonth => r"-?(?:[0-9]{4}|[1-9][0-9]{4,})-(?:0[1-9]|1[0-2])",
|
|
1305
|
+
PrimitiveType::GMonthDay => {
|
|
1306
|
+
r"--(?:02-(?:0[1-9]|1[0-9]|2[0-9])|(?:04|06|09|11)-(?:0[1-9]|[12][0-9]|30)|(?:01|03|05|07|08|10|12)-(?:0[1-9]|[12][0-9]|3[01]))"
|
|
1307
|
+
}
|
|
1308
|
+
_ => return None,
|
|
1309
|
+
};
|
|
1310
|
+
Some(format!("{body}{TZ}"))
|
|
1311
|
+
}
|
|
1312
|
+
fn name(kind: PrimitiveType) -> &'static str {
|
|
1313
|
+
match kind {
|
|
1314
|
+
PrimitiveType::GDay => "GDay",
|
|
1315
|
+
PrimitiveType::GMonth => "GMonth",
|
|
1316
|
+
PrimitiveType::GYear => "GYear",
|
|
1317
|
+
PrimitiveType::GYearMonth => "GYearMonth",
|
|
1318
|
+
PrimitiveType::GMonthDay => "GMonthDay",
|
|
1319
|
+
_ => unreachable!(),
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
fn rewrite(ty: &mut TypeRef, used: &mut HashSet<PrimitiveType>) {
|
|
1323
|
+
match ty {
|
|
1324
|
+
TypeRef::Primitive(kind) if pattern(*kind).is_some() => {
|
|
1325
|
+
used.insert(*kind);
|
|
1326
|
+
*ty = TypeRef::Named(QName::new(Some(NS), name(*kind)));
|
|
1327
|
+
}
|
|
1328
|
+
TypeRef::Boxed(inner) | TypeRef::List(inner) => rewrite(inner, used),
|
|
1329
|
+
_ => {}
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
let mut used = HashSet::new();
|
|
1333
|
+
for element in ir.elements.values_mut() {
|
|
1334
|
+
rewrite(&mut element.type_ref, &mut used);
|
|
1335
|
+
}
|
|
1336
|
+
for def in ir.types.values_mut() {
|
|
1337
|
+
match def {
|
|
1338
|
+
TypeDef::Struct(s) => {
|
|
1339
|
+
for field in &mut s.fields {
|
|
1340
|
+
rewrite(&mut field.type_ref, &mut used);
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
TypeDef::Enum(e) => rewrite(&mut e.base_type, &mut used),
|
|
1344
|
+
TypeDef::Union(u) => {
|
|
1345
|
+
for branch in &mut u.branches {
|
|
1346
|
+
rewrite(&mut branch.type_ref, &mut used);
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
TypeDef::Simple(s) => rewrite(&mut s.base_type, &mut used),
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
for kind in used {
|
|
1353
|
+
let qname = QName::new(Some(NS), name(kind));
|
|
1354
|
+
ir.types.entry(qname.clone()).or_insert_with(|| {
|
|
1355
|
+
TypeDef::Simple(Box::new(SimpleTypeDef {
|
|
1356
|
+
qname,
|
|
1357
|
+
base_type: TypeRef::Primitive(kind),
|
|
1358
|
+
facets: RestrictionFacets {
|
|
1359
|
+
patterns: vec![pattern(kind).unwrap()],
|
|
1360
|
+
..Default::default()
|
|
1361
|
+
},
|
|
1362
|
+
documentation: Some("Validated W3C Gregorian partial date".into()),
|
|
1363
|
+
}))
|
|
1364
|
+
});
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1293
1368
|
// Helpers
|
|
1294
1369
|
|
|
1295
1370
|
fn strip_prefix(s: &str) -> &str {
|
|
@@ -5,8 +5,9 @@ use std::io::Cursor;
|
|
|
5
5
|
use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
|
|
6
6
|
use quick_xml::Writer;
|
|
7
7
|
|
|
8
|
+
use crate::converters::ValueConverter;
|
|
8
9
|
use crate::error::{PolyXmlError, Result};
|
|
9
|
-
use crate::schema::{FieldKind, ModelSchema, ValueType};
|
|
10
|
+
use crate::schema::{FieldKind, ModelSchema, ScalarType, ValueType};
|
|
10
11
|
use crate::value::PolyValue;
|
|
11
12
|
|
|
12
13
|
/// XML Schema Instance namespace, needed to write `xsi:type` selectors.
|
|
@@ -219,6 +220,13 @@ impl XmlSerializer {
|
|
|
219
220
|
}
|
|
220
221
|
}
|
|
221
222
|
|
|
223
|
+
fn validate_scalar(ty: &ValueType, text: &str, field_name: &str) -> Result<()> {
|
|
224
|
+
if let ValueType::Scalar(scalar @ ScalarType::XmlGregorian(_)) = ty {
|
|
225
|
+
ValueConverter::parse_scalar(scalar, text.as_bytes(), field_name)?;
|
|
226
|
+
}
|
|
227
|
+
Ok(())
|
|
228
|
+
}
|
|
229
|
+
|
|
222
230
|
fn write_model<W: std::io::Write>(
|
|
223
231
|
writer: &mut Writer<W>,
|
|
224
232
|
tag_name: &[u8],
|
|
@@ -305,6 +313,7 @@ impl XmlSerializer {
|
|
|
305
313
|
};
|
|
306
314
|
let mut buf = [0u8; lexical_core::BUFFER_SIZE];
|
|
307
315
|
if let Some(attr_str) = Self::format_scalar_to(val, &mut buf) {
|
|
316
|
+
Self::validate_scalar(&field.val_type, attr_str, &field.name)?;
|
|
308
317
|
elem.push_attribute((attr_name.as_ref(), attr_str));
|
|
309
318
|
}
|
|
310
319
|
}
|
|
@@ -343,6 +352,7 @@ impl XmlSerializer {
|
|
|
343
352
|
if let Some(val) = get_field(text_idx, &field.name) {
|
|
344
353
|
let mut buf = [0u8; lexical_core::BUFFER_SIZE];
|
|
345
354
|
if let Some(text_content) = Self::format_scalar_to(val, &mut buf) {
|
|
355
|
+
Self::validate_scalar(&field.val_type, text_content, &field.name)?;
|
|
346
356
|
if !text_content.is_empty() {
|
|
347
357
|
writer
|
|
348
358
|
.write_event(Event::Text(BytesText::new(text_content)))
|
|
@@ -352,6 +362,9 @@ impl XmlSerializer {
|
|
|
352
362
|
for item in items {
|
|
353
363
|
let mut item_buf = [0u8; lexical_core::BUFFER_SIZE];
|
|
354
364
|
if let Some(text_content) = Self::format_scalar_to(item, &mut item_buf) {
|
|
365
|
+
if let ValueType::List(inner) = &field.val_type {
|
|
366
|
+
Self::validate_scalar(inner, text_content, &field.name)?;
|
|
367
|
+
}
|
|
355
368
|
if !text_content.is_empty() {
|
|
356
369
|
writer
|
|
357
370
|
.write_event(Event::Text(BytesText::new(text_content)))
|
|
@@ -377,6 +390,7 @@ impl XmlSerializer {
|
|
|
377
390
|
ValueType::Scalar(_) => {
|
|
378
391
|
let mut buf = [0u8; lexical_core::BUFFER_SIZE];
|
|
379
392
|
if let Some(text) = Self::format_scalar_to(val, &mut buf) {
|
|
393
|
+
Self::validate_scalar(&field.val_type, text, &field.name)?;
|
|
380
394
|
let local_child = std::str::from_utf8(&field.xml_name)?;
|
|
381
395
|
let child_tag = if let Some(ctx) = ns_ctx {
|
|
382
396
|
ctx.qualify_element(local_child, child_element_ns)
|
|
@@ -404,6 +418,7 @@ impl XmlSerializer {
|
|
|
404
418
|
if let Some(text) =
|
|
405
419
|
Self::format_scalar_to(item, &mut buf)
|
|
406
420
|
{
|
|
421
|
+
Self::validate_scalar(inner, text, &field.name)?;
|
|
407
422
|
let local_child =
|
|
408
423
|
std::str::from_utf8(&field.xml_name)?;
|
|
409
424
|
let child_tag = if let Some(ctx) = ns_ctx {
|
|
@@ -1,10 +1,59 @@
|
|
|
1
1
|
use std::sync::Arc;
|
|
2
2
|
|
|
3
3
|
use polyxml::error::PolyXmlError;
|
|
4
|
+
use polyxml::ir::PrimitiveType;
|
|
4
5
|
use polyxml::schema::{FieldKind, FieldSchema, ModelSchema, ScalarType, ValueType};
|
|
5
6
|
use polyxml::value::PolyValue;
|
|
6
7
|
use polyxml::{deserialize, serialize};
|
|
7
8
|
|
|
9
|
+
#[test]
|
|
10
|
+
fn test_gregorian_partial_date_lexical_validation() {
|
|
11
|
+
use polyxml::converters::ValueConverter;
|
|
12
|
+
|
|
13
|
+
for (kind, valid, invalid) in [
|
|
14
|
+
(PrimitiveType::GDay, "---15Z", "---32"),
|
|
15
|
+
(PrimitiveType::GMonth, "--04+14:00", "--04+14:01"),
|
|
16
|
+
(PrimitiveType::GYear, "-0045", "012345"),
|
|
17
|
+
(PrimitiveType::GYearMonth, "2026-09-05:30", "2026-13"),
|
|
18
|
+
(PrimitiveType::GMonthDay, "--02-29", "--02-30"),
|
|
19
|
+
] {
|
|
20
|
+
let scalar = ScalarType::XmlGregorian(kind);
|
|
21
|
+
assert_eq!(
|
|
22
|
+
ValueConverter::parse_scalar(&scalar, valid.as_bytes(), "date").unwrap(),
|
|
23
|
+
PolyValue::String(valid.into())
|
|
24
|
+
);
|
|
25
|
+
assert!(ValueConverter::parse_scalar(&scalar, invalid.as_bytes(), "date").is_err());
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
#[test]
|
|
30
|
+
fn test_xsd_gregorian_fields_round_trip_and_reject_invalid_dates() {
|
|
31
|
+
let xsd = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
|
32
|
+
<xs:complexType name="Calendar"><xs:sequence>
|
|
33
|
+
<xs:element name="day" type="xs:gDay"/>
|
|
34
|
+
<xs:element name="month" type="xs:gMonth"/>
|
|
35
|
+
<xs:element name="year" type="xs:gYear"/>
|
|
36
|
+
<xs:element name="yearMonth" type="xs:gYearMonth"/>
|
|
37
|
+
<xs:element name="monthDay" type="xs:gMonthDay"/>
|
|
38
|
+
</xs:sequence></xs:complexType>
|
|
39
|
+
<xs:element name="calendar" type="Calendar"/>
|
|
40
|
+
</xs:schema>"#;
|
|
41
|
+
let ir = polyxml::schema_parser::XsdParser::new()
|
|
42
|
+
.parse_str(xsd)
|
|
43
|
+
.unwrap();
|
|
44
|
+
assert_eq!(ir.types.len(), 6);
|
|
45
|
+
let schema = ModelSchema::from_ir(&ir, Some("calendar")).unwrap();
|
|
46
|
+
let xml = br#"<calendar><day>---15Z</day><month>--04+14:00</month><year>-0045</year><yearMonth>2026-09-05:30</yearMonth><monthDay>--02-29</monthDay></calendar>"#;
|
|
47
|
+
let value = deserialize(xml, Arc::clone(&schema)).unwrap();
|
|
48
|
+
let out = serialize("calendar", &value, &schema, None).unwrap();
|
|
49
|
+
assert_eq!(deserialize(&out, Arc::clone(&schema)).unwrap(), value);
|
|
50
|
+
let invalid = br#"<calendar><day>---32</day><month>--04</month><year>2026</year><yearMonth>2026-09</yearMonth><monthDay>--02-29</monthDay></calendar>"#;
|
|
51
|
+
assert!(deserialize(invalid, Arc::clone(&schema)).is_err());
|
|
52
|
+
let mut bad_value = std::collections::HashMap::new();
|
|
53
|
+
bad_value.insert("day".into(), PolyValue::String("---32".into()));
|
|
54
|
+
assert!(serialize("calendar", &PolyValue::Object(bad_value), &schema, None).is_err());
|
|
55
|
+
}
|
|
56
|
+
|
|
8
57
|
#[test]
|
|
9
58
|
fn test_all_scalar_types() {
|
|
10
59
|
let schema = ModelSchema::builder("Sensors")
|
|
@@ -26,6 +26,59 @@ const SCHEMA: &str = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
|
|
26
26
|
<xs:element name="document" type="Document"/>
|
|
27
27
|
</xs:schema>"#;
|
|
28
28
|
|
|
29
|
+
const GREGORIAN_SCHEMA: &str = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
|
30
|
+
<xs:complexType name="Calendar"><xs:sequence>
|
|
31
|
+
<xs:element name="day" type="xs:gDay"/>
|
|
32
|
+
<xs:element name="month" type="xs:gMonth"/>
|
|
33
|
+
<xs:element name="year" type="xs:gYear"/>
|
|
34
|
+
<xs:element name="yearMonth" type="xs:gYearMonth"/>
|
|
35
|
+
<xs:element name="monthDay" type="xs:gMonthDay"/>
|
|
36
|
+
</xs:sequence></xs:complexType>
|
|
37
|
+
</xs:schema>"#;
|
|
38
|
+
|
|
39
|
+
#[test]
|
|
40
|
+
fn gregorian_primitives_emit_validated_types_across_targets() {
|
|
41
|
+
let ir = XsdParser::new().parse_str(GREGORIAN_SCHEMA).unwrap();
|
|
42
|
+
assert_eq!(ir.types.len(), 6);
|
|
43
|
+
let rust = RustCodegen::new(RustOptions::default()).generate_module(&ir);
|
|
44
|
+
assert!(rust.contains("validate_GDay_patterns"));
|
|
45
|
+
assert!(rust.contains("validate_GMonthDay_patterns"));
|
|
46
|
+
let go = GoCodegen::new(GoOptions::default()).generate_module(&ir);
|
|
47
|
+
assert!(go.contains("func (s GDay) Validate() error"));
|
|
48
|
+
assert!(go.contains("func (s *GMonthDay) UnmarshalText(text []byte) error"));
|
|
49
|
+
let cpp = CppCodegen::new(CppOptions::default()).generate_header(&ir);
|
|
50
|
+
assert!(cpp.contains("validate_GDay_patterns"));
|
|
51
|
+
let java = JavaCodegen::new(JavaOptions::default()).generate_module(&ir, "Models");
|
|
52
|
+
assert!(java.contains("Pattern.compile(\"---"));
|
|
53
|
+
let python = PythonCodegen::new(PythonOptions {
|
|
54
|
+
backend: PythonBackend::Pydantic,
|
|
55
|
+
..Default::default()
|
|
56
|
+
})
|
|
57
|
+
.generate_module(&ir);
|
|
58
|
+
assert!(python.contains("type GDay = Annotated[str, Field(pattern="));
|
|
59
|
+
let ts = TypeScriptCodegen::new(TypeScriptOptions {
|
|
60
|
+
backend: TypeScriptBackend::Zod,
|
|
61
|
+
..Default::default()
|
|
62
|
+
})
|
|
63
|
+
.generate_module(&ir);
|
|
64
|
+
assert!(ts.contains("GDaySchema = z.string().regex("));
|
|
65
|
+
let valibot = TypeScriptCodegen::new(TypeScriptOptions {
|
|
66
|
+
backend: TypeScriptBackend::Valibot,
|
|
67
|
+
..Default::default()
|
|
68
|
+
})
|
|
69
|
+
.generate_module(&ir);
|
|
70
|
+
assert!(valibot.contains("GDaySchema = v.pipe(v.string(), v.regex("));
|
|
71
|
+
let typebox = TypeScriptCodegen::new(TypeScriptOptions {
|
|
72
|
+
backend: TypeScriptBackend::TypeBox,
|
|
73
|
+
..Default::default()
|
|
74
|
+
})
|
|
75
|
+
.generate_module(&ir);
|
|
76
|
+
assert!(typebox.contains("GDaySchema = Type.String({ pattern:"));
|
|
77
|
+
let csharp = CSharpCodegen::new(CSharpOptions::default()).generate_module(&ir);
|
|
78
|
+
assert!(csharp.contains("record GDay("));
|
|
79
|
+
assert!(csharp.contains("Regex.IsMatch(Value.ToString()"));
|
|
80
|
+
}
|
|
81
|
+
|
|
29
82
|
fn schema() -> SchemaIR {
|
|
30
83
|
let dir = tempdir().unwrap();
|
|
31
84
|
let path = dir.path().join("pattern.xsd");
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
use crate::error::{PolyXmlError, Result};
|
|
2
|
-
use crate::schema::ScalarType;
|
|
3
|
-
use crate::value::PolyValue;
|
|
4
|
-
|
|
5
|
-
#[inline(always)]
|
|
6
|
-
pub fn trim_bytes(mut b: &[u8]) -> &[u8] {
|
|
7
|
-
while let Some((first, rest)) = b.split_first() {
|
|
8
|
-
if first.is_ascii_whitespace() {
|
|
9
|
-
b = rest;
|
|
10
|
-
} else {
|
|
11
|
-
break;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
while let Some((last, rest)) = b.split_last() {
|
|
15
|
-
if last.is_ascii_whitespace() {
|
|
16
|
-
b = rest;
|
|
17
|
-
} else {
|
|
18
|
-
break;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
b
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
pub struct ValueConverter;
|
|
25
|
-
|
|
26
|
-
impl ValueConverter {
|
|
27
|
-
pub fn parse_scalar(
|
|
28
|
-
scalar_type: &ScalarType,
|
|
29
|
-
bytes: &[u8],
|
|
30
|
-
field_name: &str,
|
|
31
|
-
) -> Result<PolyValue> {
|
|
32
|
-
match scalar_type {
|
|
33
|
-
ScalarType::String => {
|
|
34
|
-
let s = std::str::from_utf8(bytes)?;
|
|
35
|
-
Ok(PolyValue::String(s.to_string()))
|
|
36
|
-
}
|
|
37
|
-
ScalarType::Int => {
|
|
38
|
-
let trimmed = trim_bytes(bytes);
|
|
39
|
-
let val: i64 =
|
|
40
|
-
lexical_core::parse(trimmed).map_err(|_| PolyXmlError::ScalarParseError {
|
|
41
|
-
field: field_name.to_string(),
|
|
42
|
-
expected: "integer",
|
|
43
|
-
value: String::from_utf8_lossy(bytes).to_string(),
|
|
44
|
-
})?;
|
|
45
|
-
Ok(PolyValue::Int(val))
|
|
46
|
-
}
|
|
47
|
-
ScalarType::Float => {
|
|
48
|
-
let trimmed = trim_bytes(bytes);
|
|
49
|
-
let val: f64 =
|
|
50
|
-
lexical_core::parse(trimmed).map_err(|_| PolyXmlError::ScalarParseError {
|
|
51
|
-
field: field_name.to_string(),
|
|
52
|
-
expected: "float",
|
|
53
|
-
value: String::from_utf8_lossy(bytes).to_string(),
|
|
54
|
-
})?;
|
|
55
|
-
Ok(PolyValue::Float(val))
|
|
56
|
-
}
|
|
57
|
-
ScalarType::Bool => {
|
|
58
|
-
let trimmed = trim_bytes(bytes);
|
|
59
|
-
match trimmed {
|
|
60
|
-
b"true" | b"1" => Ok(PolyValue::Bool(true)),
|
|
61
|
-
b"false" | b"0" => Ok(PolyValue::Bool(false)),
|
|
62
|
-
_ => Err(PolyXmlError::ScalarParseError {
|
|
63
|
-
field: field_name.to_string(),
|
|
64
|
-
expected: "boolean ('true', 'false', '1', '0')",
|
|
65
|
-
value: String::from_utf8_lossy(bytes).to_string(),
|
|
66
|
-
}),
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
ScalarType::Decimal
|
|
70
|
-
| ScalarType::XmlDate
|
|
71
|
-
| ScalarType::XmlDateTime
|
|
72
|
-
| ScalarType::XmlTime
|
|
73
|
-
| ScalarType::XmlDuration
|
|
74
|
-
| ScalarType::Any => {
|
|
75
|
-
let s = std::str::from_utf8(trim_bytes(bytes))?;
|
|
76
|
-
Ok(PolyValue::String(s.to_string()))
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{polyxml-0.26.0 → polyxml-0.27.0}/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
|