polyxml 0.19.0__tar.gz → 0.19.2__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.19.0 → polyxml-0.19.2}/Cargo.lock +5 -5
- {polyxml-0.19.0 → polyxml-0.19.2}/Cargo.toml +1 -1
- {polyxml-0.19.0 → polyxml-0.19.2}/PKG-INFO +1 -1
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/src/codegen/csharp/mod.rs +3 -3
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/src/codegen/python/mod.rs +13 -2
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/src/json.rs +20 -3
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/src/parser.rs +25 -20
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/src/serializer.rs +17 -9
- polyxml-0.19.2/crates/polyxml-core/src/value.rs +167 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/tests/test_csharp_codegen.rs +1 -1
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-python/src/lib.rs +294 -37
- {polyxml-0.19.0 → polyxml-0.19.2}/pyproject.toml +1 -1
- polyxml-0.19.0/crates/polyxml-core/src/value.rs +0 -90
- {polyxml-0.19.0 → polyxml-0.19.2}/README.md +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/Cargo.toml +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/README.md +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/benches/core_benchmarks.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/src/codegen/cpp/mod.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/src/codegen/go/mod.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/src/codegen/java/mod.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/src/codegen/mod.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/src/codegen/rust/mod.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/src/codegen/typescript/mod.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/src/converters.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/src/error.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/src/ir/mod.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/src/ir/tarjan.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/src/lib.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/src/schema.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/src/schema_parser/mod.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/src/transcoder.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/tests/test_conformance.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/tests/test_core.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/tests/test_cpp_codegen.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/tests/test_go_codegen.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/tests/test_java_codegen.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/tests/test_json.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/tests/test_python_codegen.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/tests/test_rust_codecs.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/tests/test_rust_codegen.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/tests/test_schema_ir.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/tests/test_security.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/tests/test_transcoder.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-core/tests/test_ts_codegen.rs +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-python/Cargo.toml +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-python/README.md +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-python/tests/test_conformance.py +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-python/tests/test_generated_models.py +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-python/tests/test_json.py +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/crates/polyxml-python/tests/test_polyxml.py +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/python/polyxml/AGENT_GUIDE.md +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/python/polyxml/__init__.py +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/python/polyxml/compat/__init__.py +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/python/polyxml/compat/xsdata.py +0 -0
- {polyxml-0.19.0 → polyxml-0.19.2}/python/polyxml/py.typed +0 -0
|
@@ -549,7 +549,7 @@ dependencies = [
|
|
|
549
549
|
|
|
550
550
|
[[package]]
|
|
551
551
|
name = "polyxml"
|
|
552
|
-
version = "0.19.
|
|
552
|
+
version = "0.19.2"
|
|
553
553
|
dependencies = [
|
|
554
554
|
"criterion",
|
|
555
555
|
"heck",
|
|
@@ -567,14 +567,14 @@ dependencies = [
|
|
|
567
567
|
|
|
568
568
|
[[package]]
|
|
569
569
|
name = "polyxml-c"
|
|
570
|
-
version = "0.19.
|
|
570
|
+
version = "0.19.2"
|
|
571
571
|
dependencies = [
|
|
572
572
|
"polyxml",
|
|
573
573
|
]
|
|
574
574
|
|
|
575
575
|
[[package]]
|
|
576
576
|
name = "polyxml-cli"
|
|
577
|
-
version = "0.19.
|
|
577
|
+
version = "0.19.2"
|
|
578
578
|
dependencies = [
|
|
579
579
|
"clap",
|
|
580
580
|
"glob",
|
|
@@ -588,7 +588,7 @@ dependencies = [
|
|
|
588
588
|
|
|
589
589
|
[[package]]
|
|
590
590
|
name = "polyxml-js"
|
|
591
|
-
version = "0.19.
|
|
591
|
+
version = "0.19.2"
|
|
592
592
|
dependencies = [
|
|
593
593
|
"napi",
|
|
594
594
|
"napi-build",
|
|
@@ -598,7 +598,7 @@ dependencies = [
|
|
|
598
598
|
|
|
599
599
|
[[package]]
|
|
600
600
|
name = "polyxml-python"
|
|
601
|
-
version = "0.19.
|
|
601
|
+
version = "0.19.2"
|
|
602
602
|
dependencies = [
|
|
603
603
|
"lexical-core",
|
|
604
604
|
"polyxml",
|
|
@@ -391,8 +391,8 @@ impl CSharpCodegen {
|
|
|
391
391
|
if self.options.emit_json_attributes {
|
|
392
392
|
writeln!(
|
|
393
393
|
out,
|
|
394
|
-
"{}[JsonConverter(typeof(JsonStringEnumConverter))]",
|
|
395
|
-
indent
|
|
394
|
+
"{}[JsonConverter(typeof(JsonStringEnumConverter<{}>))]",
|
|
395
|
+
indent, enum_name
|
|
396
396
|
)
|
|
397
397
|
.unwrap();
|
|
398
398
|
}
|
|
@@ -669,7 +669,7 @@ impl CSharpCodegen {
|
|
|
669
669
|
|| f.nillable
|
|
670
670
|
|| (f.cardinality.is_list() && f.cardinality.min_occurs == 0);
|
|
671
671
|
let default_arg = if is_opt {
|
|
672
|
-
"default
|
|
672
|
+
format!("default({})", field_type)
|
|
673
673
|
} else {
|
|
674
674
|
format!("default({})!", field_type)
|
|
675
675
|
};
|
|
@@ -204,8 +204,19 @@ impl PythonCodegen {
|
|
|
204
204
|
if let Some(ref header) = self.options.custom_header {
|
|
205
205
|
let trimmed = header.trim();
|
|
206
206
|
if !trimmed.is_empty() {
|
|
207
|
-
|
|
208
|
-
|
|
207
|
+
for line in trimmed.lines() {
|
|
208
|
+
let line_trimmed = line.trim_start();
|
|
209
|
+
if line_trimmed.starts_with("//") {
|
|
210
|
+
let content = line_trimmed.strip_prefix("//").unwrap_or("");
|
|
211
|
+
out.push('#');
|
|
212
|
+
out.push_str(content);
|
|
213
|
+
out.push('\n');
|
|
214
|
+
} else {
|
|
215
|
+
out.push_str(line);
|
|
216
|
+
out.push('\n');
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
out.push('\n');
|
|
209
220
|
}
|
|
210
221
|
}
|
|
211
222
|
|
|
@@ -27,6 +27,15 @@ impl From<&PolyValue> for JsonValue {
|
|
|
27
27
|
}
|
|
28
28
|
JsonValue::Object(map)
|
|
29
29
|
}
|
|
30
|
+
PolyValue::Record { schema, values } => {
|
|
31
|
+
let mut map = serde_json::Map::with_capacity(schema.fields.len());
|
|
32
|
+
for (idx, field) in schema.fields.iter().enumerate() {
|
|
33
|
+
if let Some(Some(v)) = values.get(idx) {
|
|
34
|
+
map.insert(field.name.clone(), JsonValue::from(v));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
JsonValue::Object(map)
|
|
38
|
+
}
|
|
30
39
|
}
|
|
31
40
|
}
|
|
32
41
|
}
|
|
@@ -67,17 +76,25 @@ pub fn poly_value_to_json_value(
|
|
|
67
76
|
schema: &ModelSchema,
|
|
68
77
|
by_alias: bool,
|
|
69
78
|
) -> JsonValue {
|
|
79
|
+
let get_field = |idx: usize, name: &str| -> Option<&PolyValue> {
|
|
80
|
+
match val {
|
|
81
|
+
PolyValue::Record { values, .. } => values.get(idx).and_then(|v| v.as_ref()),
|
|
82
|
+
PolyValue::Object(map) => map.get(name),
|
|
83
|
+
_ => None,
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
70
87
|
match val {
|
|
71
|
-
PolyValue::Object(
|
|
88
|
+
PolyValue::Record { .. } | PolyValue::Object(_) => {
|
|
72
89
|
let mut json_map = serde_json::Map::with_capacity(schema.fields.len());
|
|
73
|
-
for field in
|
|
90
|
+
for (idx, field) in schema.fields.iter().enumerate() {
|
|
74
91
|
let key = if by_alias {
|
|
75
92
|
std::str::from_utf8(&field.xml_name).unwrap_or(field.name.as_str())
|
|
76
93
|
} else {
|
|
77
94
|
field.name.as_str()
|
|
78
95
|
};
|
|
79
96
|
|
|
80
|
-
if let Some(field_val) =
|
|
97
|
+
if let Some(field_val) = get_field(idx, &field.name) {
|
|
81
98
|
let converted = match &field.val_type {
|
|
82
99
|
ValueType::Nested(nested_schema) => {
|
|
83
100
|
if field_val.is_null() {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
use std::collections::HashMap;
|
|
2
1
|
use std::sync::Arc;
|
|
3
2
|
|
|
4
3
|
use quick_xml::events::{BytesStart, Event};
|
|
@@ -48,19 +47,10 @@ impl StackFrame {
|
|
|
48
47
|
}
|
|
49
48
|
|
|
50
49
|
fn finish(mut self) -> Result<PolyValue> {
|
|
51
|
-
let mut obj = HashMap::with_capacity(self.schema.fields.len());
|
|
52
|
-
|
|
53
|
-
for (idx, field) in self.schema.fields.iter().enumerate() {
|
|
54
|
-
if let Some(val) = self.values.get_mut(idx).and_then(|v| v.take()) {
|
|
55
|
-
obj.insert(field.name.clone(), val);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
50
|
if !self.list_values.is_empty() {
|
|
60
51
|
for (idx, list_opt) in self.list_values.into_iter().enumerate() {
|
|
61
52
|
if let Some(list) = list_opt {
|
|
62
|
-
|
|
63
|
-
obj.insert(field.name.clone(), PolyValue::List(list));
|
|
53
|
+
self.values[idx] = Some(PolyValue::List(list));
|
|
64
54
|
}
|
|
65
55
|
}
|
|
66
56
|
}
|
|
@@ -72,12 +62,12 @@ impl StackFrame {
|
|
|
72
62
|
match &field.val_type {
|
|
73
63
|
ValueType::Scalar(st) => {
|
|
74
64
|
let val = ValueConverter::parse_scalar(st, text_buf, &field.name)?;
|
|
75
|
-
|
|
65
|
+
self.values[text_idx] = Some(val);
|
|
76
66
|
}
|
|
77
67
|
ValueType::List(inner) => {
|
|
78
68
|
if let ValueType::Scalar(st) = &**inner {
|
|
79
69
|
let val = ValueConverter::parse_scalar(st, text_buf, &field.name)?;
|
|
80
|
-
|
|
70
|
+
self.values[text_idx] = Some(PolyValue::List(vec![val]));
|
|
81
71
|
}
|
|
82
72
|
}
|
|
83
73
|
_ => {}
|
|
@@ -86,7 +76,10 @@ impl StackFrame {
|
|
|
86
76
|
}
|
|
87
77
|
}
|
|
88
78
|
|
|
89
|
-
Ok(PolyValue::
|
|
79
|
+
Ok(PolyValue::Record {
|
|
80
|
+
schema: self.schema,
|
|
81
|
+
values: self.values.into_vec().into_boxed_slice(),
|
|
82
|
+
})
|
|
90
83
|
}
|
|
91
84
|
}
|
|
92
85
|
|
|
@@ -313,12 +306,24 @@ impl XmlDeserializer {
|
|
|
313
306
|
if unknown_depth > 0 {
|
|
314
307
|
continue;
|
|
315
308
|
}
|
|
316
|
-
let
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
309
|
+
let raw = e.as_ref();
|
|
310
|
+
let raw_bytes = raw.as_bytes();
|
|
311
|
+
if memchr::memchr(b'&', raw_bytes).is_none() {
|
|
312
|
+
if active_scalar_field.is_some() {
|
|
313
|
+
text_buf.extend_from_slice(raw_bytes);
|
|
314
|
+
} else if let Some(frame) = stack.last_mut() {
|
|
315
|
+
if let Some(ref mut tb) = frame.frame_text_buf {
|
|
316
|
+
tb.extend_from_slice(raw_bytes);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
} else {
|
|
320
|
+
let unescaped = quick_xml::escape::unescape(raw)?;
|
|
321
|
+
if active_scalar_field.is_some() {
|
|
322
|
+
text_buf.extend_from_slice(unescaped.as_bytes());
|
|
323
|
+
} else if let Some(frame) = stack.last_mut() {
|
|
324
|
+
if let Some(ref mut tb) = frame.frame_text_buf {
|
|
325
|
+
tb.extend_from_slice(unescaped.as_bytes());
|
|
326
|
+
}
|
|
322
327
|
}
|
|
323
328
|
}
|
|
324
329
|
}
|
|
@@ -212,12 +212,20 @@ impl XmlSerializer {
|
|
|
212
212
|
is_root: bool,
|
|
213
213
|
element_ns: Option<&str>,
|
|
214
214
|
) -> Result<()> {
|
|
215
|
-
|
|
216
|
-
PolyValue::Object(
|
|
215
|
+
match value {
|
|
216
|
+
PolyValue::Record { .. } | PolyValue::Object(_) => {}
|
|
217
217
|
_ => {
|
|
218
218
|
return Err(PolyXmlError::SerializationError(
|
|
219
|
-
"Expected Object value for model".into(),
|
|
220
|
-
))
|
|
219
|
+
"Expected Object or Record value for model".into(),
|
|
220
|
+
));
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
let get_field = |idx: usize, name: &str| -> Option<&PolyValue> {
|
|
225
|
+
match value {
|
|
226
|
+
PolyValue::Record { values, .. } => values.get(idx).and_then(|v| v.as_ref()),
|
|
227
|
+
PolyValue::Object(o) => o.get(name),
|
|
228
|
+
_ => None,
|
|
221
229
|
}
|
|
222
230
|
};
|
|
223
231
|
|
|
@@ -245,9 +253,9 @@ impl XmlSerializer {
|
|
|
245
253
|
}
|
|
246
254
|
|
|
247
255
|
// 1. Collect and write attributes
|
|
248
|
-
for field in
|
|
256
|
+
for (idx, field) in schema.fields.iter().enumerate() {
|
|
249
257
|
if field.kind == FieldKind::Attribute {
|
|
250
|
-
if let Some(val) =
|
|
258
|
+
if let Some(val) = get_field(idx, &field.name) {
|
|
251
259
|
if !val.is_null() {
|
|
252
260
|
let local_attr = std::str::from_utf8(&field.xml_name)?;
|
|
253
261
|
let attr_name = if let Some(ctx) = ns_ctx {
|
|
@@ -271,7 +279,7 @@ impl XmlSerializer {
|
|
|
271
279
|
// 2. Write text content if present
|
|
272
280
|
if let Some(text_idx) = schema.text_field {
|
|
273
281
|
let field = &schema.fields[text_idx];
|
|
274
|
-
if let Some(val) =
|
|
282
|
+
if let Some(val) = get_field(text_idx, &field.name) {
|
|
275
283
|
let mut buf = [0u8; lexical_core::BUFFER_SIZE];
|
|
276
284
|
if let Some(text_content) = Self::format_scalar_to(val, &mut buf) {
|
|
277
285
|
if !text_content.is_empty() {
|
|
@@ -295,9 +303,9 @@ impl XmlSerializer {
|
|
|
295
303
|
}
|
|
296
304
|
|
|
297
305
|
// 3. Write child elements
|
|
298
|
-
for field in
|
|
306
|
+
for (idx, field) in schema.fields.iter().enumerate() {
|
|
299
307
|
if field.kind == FieldKind::Element {
|
|
300
|
-
if let Some(val) =
|
|
308
|
+
if let Some(val) = get_field(idx, &field.name) {
|
|
301
309
|
if val.is_null() {
|
|
302
310
|
continue;
|
|
303
311
|
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
use std::collections::HashMap;
|
|
2
|
+
use std::sync::Arc;
|
|
3
|
+
|
|
4
|
+
use crate::schema::ModelSchema;
|
|
5
|
+
|
|
6
|
+
#[derive(Debug, Clone)]
|
|
7
|
+
pub enum PolyValue {
|
|
8
|
+
Null,
|
|
9
|
+
Bool(bool),
|
|
10
|
+
Int(i64),
|
|
11
|
+
Float(f64),
|
|
12
|
+
String(String),
|
|
13
|
+
List(Vec<PolyValue>),
|
|
14
|
+
Object(HashMap<String, PolyValue>),
|
|
15
|
+
Record {
|
|
16
|
+
schema: Arc<ModelSchema>,
|
|
17
|
+
values: Box<[Option<PolyValue>]>,
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
impl PartialEq for PolyValue {
|
|
22
|
+
fn eq(&self, other: &Self) -> bool {
|
|
23
|
+
match (self, other) {
|
|
24
|
+
(PolyValue::Null, PolyValue::Null) => true,
|
|
25
|
+
(PolyValue::Bool(a), PolyValue::Bool(b)) => a == b,
|
|
26
|
+
(PolyValue::Int(a), PolyValue::Int(b)) => a == b,
|
|
27
|
+
(PolyValue::Float(a), PolyValue::Float(b)) => a == b,
|
|
28
|
+
(PolyValue::String(a), PolyValue::String(b)) => a == b,
|
|
29
|
+
(PolyValue::List(a), PolyValue::List(b)) => a == b,
|
|
30
|
+
(PolyValue::Object(a), PolyValue::Object(b)) => a == b,
|
|
31
|
+
(
|
|
32
|
+
PolyValue::Record {
|
|
33
|
+
schema: s1,
|
|
34
|
+
values: v1,
|
|
35
|
+
},
|
|
36
|
+
PolyValue::Record {
|
|
37
|
+
schema: s2,
|
|
38
|
+
values: v2,
|
|
39
|
+
},
|
|
40
|
+
) => (Arc::ptr_eq(s1, s2) || s1.name == s2.name) && v1 == v2,
|
|
41
|
+
(PolyValue::Record { schema, values }, PolyValue::Object(map))
|
|
42
|
+
| (PolyValue::Object(map), PolyValue::Record { schema, values }) => {
|
|
43
|
+
for (idx, field) in schema.fields.iter().enumerate() {
|
|
44
|
+
let rec_val = values.get(idx).and_then(|v| v.as_ref());
|
|
45
|
+
let map_val = map.get(&field.name);
|
|
46
|
+
if rec_val != map_val {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
true
|
|
51
|
+
}
|
|
52
|
+
_ => false,
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
impl PolyValue {
|
|
58
|
+
pub fn is_null(&self) -> bool {
|
|
59
|
+
matches!(self, PolyValue::Null)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
pub fn as_str(&self) -> Option<&str> {
|
|
63
|
+
match self {
|
|
64
|
+
PolyValue::String(s) => Some(s.as_str()),
|
|
65
|
+
_ => None,
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
pub fn as_i64(&self) -> Option<i64> {
|
|
70
|
+
match self {
|
|
71
|
+
PolyValue::Int(i) => Some(*i),
|
|
72
|
+
_ => None,
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
pub fn as_f64(&self) -> Option<f64> {
|
|
77
|
+
match self {
|
|
78
|
+
PolyValue::Float(f) => Some(*f),
|
|
79
|
+
_ => None,
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
pub fn as_bool(&self) -> Option<bool> {
|
|
84
|
+
match self {
|
|
85
|
+
PolyValue::Bool(b) => Some(*b),
|
|
86
|
+
_ => None,
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
pub fn as_list(&self) -> Option<&[PolyValue]> {
|
|
91
|
+
match self {
|
|
92
|
+
PolyValue::List(l) => Some(l.as_slice()),
|
|
93
|
+
_ => None,
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
pub fn as_object(&self) -> Option<&HashMap<String, PolyValue>> {
|
|
98
|
+
match self {
|
|
99
|
+
PolyValue::Object(o) => Some(o),
|
|
100
|
+
_ => None,
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
pub fn as_record(&self) -> Option<(&Arc<ModelSchema>, &[Option<PolyValue>])> {
|
|
105
|
+
match self {
|
|
106
|
+
PolyValue::Record { schema, values } => Some((schema, values.as_ref())),
|
|
107
|
+
_ => None,
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
pub fn get_by_index(&self, idx: usize) -> Option<&PolyValue> {
|
|
112
|
+
match self {
|
|
113
|
+
PolyValue::Record { values, .. } => values.get(idx).and_then(|v| v.as_ref()),
|
|
114
|
+
_ => None,
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
pub fn get(&self, key: &str) -> Option<&PolyValue> {
|
|
119
|
+
match self {
|
|
120
|
+
PolyValue::Object(o) => o.get(key),
|
|
121
|
+
PolyValue::Record { schema, values } => {
|
|
122
|
+
let idx = schema.fields.iter().position(|f| f.name == key)?;
|
|
123
|
+
values.get(idx).and_then(|v| v.as_ref())
|
|
124
|
+
}
|
|
125
|
+
_ => None,
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
pub fn to_hash_map(&self) -> Option<HashMap<String, PolyValue>> {
|
|
130
|
+
match self {
|
|
131
|
+
PolyValue::Object(o) => Some(o.clone()),
|
|
132
|
+
PolyValue::Record { schema, values } => {
|
|
133
|
+
let mut map = HashMap::with_capacity(schema.fields.len());
|
|
134
|
+
for (idx, field) in schema.fields.iter().enumerate() {
|
|
135
|
+
if let Some(Some(val)) = values.get(idx) {
|
|
136
|
+
map.insert(field.name.clone(), val.clone());
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
Some(map)
|
|
140
|
+
}
|
|
141
|
+
_ => None,
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/// Serialize this PolyValue into raw JSON bytes.
|
|
146
|
+
pub fn to_json(&self, indent: Option<usize>) -> crate::error::Result<Vec<u8>> {
|
|
147
|
+
let json_val = serde_json::Value::from(self);
|
|
148
|
+
match indent {
|
|
149
|
+
Some(spaces) if spaces > 0 => {
|
|
150
|
+
let indent_str = " ".repeat(spaces);
|
|
151
|
+
let formatter =
|
|
152
|
+
serde_json::ser::PrettyFormatter::with_indent(indent_str.as_bytes());
|
|
153
|
+
let mut buf = Vec::new();
|
|
154
|
+
let mut ser = serde_json::Serializer::with_formatter(&mut buf, formatter);
|
|
155
|
+
serde::Serialize::serialize(&json_val, &mut ser)?;
|
|
156
|
+
Ok(buf)
|
|
157
|
+
}
|
|
158
|
+
_ => Ok(serde_json::to_vec(&json_val)?),
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/// Deserialize raw JSON bytes into a dynamic PolyValue without a schema.
|
|
163
|
+
pub fn from_json(json_bytes: &[u8]) -> crate::error::Result<Self> {
|
|
164
|
+
let json_val: serde_json::Value = serde_json::from_slice(json_bytes)?;
|
|
165
|
+
Ok(PolyValue::from(json_val))
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -180,7 +180,7 @@ fn test_csharp_records_and_enums_generation() {
|
|
|
180
180
|
|
|
181
181
|
assert!(cs_code.contains("using System.Text.Json.Serialization;"));
|
|
182
182
|
assert!(cs_code.contains("namespace Crm.Models;"));
|
|
183
|
-
assert!(cs_code.contains("[JsonConverter(typeof(JsonStringEnumConverter))]"));
|
|
183
|
+
assert!(cs_code.contains("[JsonConverter(typeof(JsonStringEnumConverter<OrderStatus>))]"));
|
|
184
184
|
assert!(cs_code.contains("public enum OrderStatus"));
|
|
185
185
|
assert!(cs_code.contains("[XmlEnum(\"pending\")]"));
|
|
186
186
|
assert!(cs_code.contains("Pending,"));
|
|
@@ -12,11 +12,14 @@ use std::sync::{Arc, RwLock};
|
|
|
12
12
|
use polyxml::schema::{FieldKind, FieldSchema, ModelSchema, ScalarType, ValueType};
|
|
13
13
|
use polyxml::value::PolyValue;
|
|
14
14
|
|
|
15
|
+
use smallvec::SmallVec;
|
|
16
|
+
|
|
15
17
|
type PyObject = Py<PyAny>;
|
|
16
18
|
|
|
17
19
|
struct CachedFieldMeta {
|
|
18
20
|
py_string: Py<PyString>,
|
|
19
21
|
is_init: bool,
|
|
22
|
+
#[allow(dead_code)]
|
|
20
23
|
is_kw_only: bool,
|
|
21
24
|
py_type: Option<PyObject>,
|
|
22
25
|
}
|
|
@@ -25,6 +28,8 @@ struct CachedSchemaMeta {
|
|
|
25
28
|
schema: Arc<ModelSchema>,
|
|
26
29
|
py_cls: PyObject,
|
|
27
30
|
is_dataclass: bool,
|
|
31
|
+
all_init: bool,
|
|
32
|
+
kwnames: Option<Py<PyTuple>>,
|
|
28
33
|
fields: Vec<CachedFieldMeta>,
|
|
29
34
|
}
|
|
30
35
|
|
|
@@ -394,10 +399,24 @@ fn get_or_create_schema_meta<'py>(cls: &Bound<'py, PyType>) -> PyResult<Arc<Cach
|
|
|
394
399
|
let (schema, cached_fields) = extract_schema_from_class(cls)?;
|
|
395
400
|
let py_cls_obj: PyObject = cls.clone().into_any().unbind();
|
|
396
401
|
|
|
402
|
+
let all_init =
|
|
403
|
+
is_dataclass && !cached_fields.is_empty() && cached_fields.iter().all(|f| f.is_init);
|
|
404
|
+
let kwnames = if all_init {
|
|
405
|
+
let py_strings: Vec<Bound<'py, PyString>> = cached_fields
|
|
406
|
+
.iter()
|
|
407
|
+
.map(|f| f.py_string.bind(cls.py()).clone())
|
|
408
|
+
.collect();
|
|
409
|
+
PyTuple::new(cls.py(), &py_strings).ok().map(|t| t.unbind())
|
|
410
|
+
} else {
|
|
411
|
+
None
|
|
412
|
+
};
|
|
413
|
+
|
|
397
414
|
let meta = Arc::new(CachedSchemaMeta {
|
|
398
415
|
schema: Arc::clone(&schema),
|
|
399
416
|
py_cls: py_cls_obj,
|
|
400
417
|
is_dataclass,
|
|
418
|
+
all_init,
|
|
419
|
+
kwnames,
|
|
401
420
|
fields: cached_fields,
|
|
402
421
|
});
|
|
403
422
|
|
|
@@ -559,7 +578,10 @@ fn poly_value_to_py<'py>(
|
|
|
559
578
|
Ok(py_list.into_any().unbind())
|
|
560
579
|
}
|
|
561
580
|
}
|
|
562
|
-
PolyValue::
|
|
581
|
+
PolyValue::Record {
|
|
582
|
+
schema: rec_schema,
|
|
583
|
+
values,
|
|
584
|
+
} => {
|
|
563
585
|
let meta_opt = if let ValueType::Nested(ref s) = val_type {
|
|
564
586
|
lookup_cached_meta(&s.name)
|
|
565
587
|
} else {
|
|
@@ -573,40 +595,194 @@ fn poly_value_to_py<'py>(
|
|
|
573
595
|
|
|
574
596
|
if let Some(ref target_cls) = effective_cls {
|
|
575
597
|
if let ValueType::Nested(ref schema) = val_type {
|
|
576
|
-
// Method 1:
|
|
598
|
+
// Method 1: Python 3.12+ Vectorcall for Dataclasses
|
|
577
599
|
if let Some(ref meta) = meta_opt {
|
|
578
|
-
if meta.is_dataclass
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
{
|
|
588
|
-
|
|
600
|
+
if meta.is_dataclass && meta.all_init && !meta.fields.is_empty() {
|
|
601
|
+
if let Some(ref kwnames) = meta.kwnames {
|
|
602
|
+
let mut args_ptrs: SmallVec<[*mut pyo3::ffi::PyObject; 16]> =
|
|
603
|
+
SmallVec::with_capacity(schema.fields.len());
|
|
604
|
+
let mut py_vals: SmallVec<[PyObject; 16]> =
|
|
605
|
+
SmallVec::with_capacity(schema.fields.len());
|
|
606
|
+
let mut all_found = true;
|
|
607
|
+
|
|
608
|
+
for (i, field) in schema.fields.iter().enumerate() {
|
|
609
|
+
if let Some(Some(field_val)) = values.get(i) {
|
|
610
|
+
let field_cls =
|
|
611
|
+
if let ValueType::Nested(ref s) = field.val_type {
|
|
612
|
+
lookup_py_class(py, &s.name)
|
|
613
|
+
} else {
|
|
614
|
+
None
|
|
615
|
+
};
|
|
616
|
+
let f_meta = meta.fields.get(i);
|
|
617
|
+
let py_val = poly_value_to_py(
|
|
618
|
+
py,
|
|
619
|
+
field_val,
|
|
620
|
+
&field.val_type,
|
|
621
|
+
field_cls.as_ref(),
|
|
622
|
+
f_meta,
|
|
623
|
+
)?;
|
|
624
|
+
args_ptrs.push(py_val.as_ptr());
|
|
625
|
+
py_vals.push(py_val);
|
|
589
626
|
} else {
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
627
|
+
all_found = false;
|
|
628
|
+
break;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
if all_found && args_ptrs.len() == schema.fields.len() {
|
|
633
|
+
unsafe {
|
|
634
|
+
let res_ptr = pyo3::ffi::PyObject_Vectorcall(
|
|
635
|
+
target_cls.as_ptr(),
|
|
636
|
+
args_ptrs.as_ptr(),
|
|
637
|
+
0,
|
|
638
|
+
kwnames.as_ptr(),
|
|
639
|
+
);
|
|
640
|
+
if !res_ptr.is_null() {
|
|
641
|
+
return Ok(Bound::from_owned_ptr(py, res_ptr).unbind());
|
|
642
|
+
} else {
|
|
643
|
+
return Err(PyErr::fetch(py));
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// Method 2: Keyword arguments with init=False post-init support
|
|
652
|
+
let kwargs = PyDict::new(py);
|
|
653
|
+
let mut post_init_fields: Vec<(Py<PyString>, PyObject)> = Vec::new();
|
|
654
|
+
|
|
655
|
+
for (i, field) in schema.fields.iter().enumerate() {
|
|
656
|
+
if let Some(Some(field_val)) = values.get(i) {
|
|
657
|
+
let field_cls = if let ValueType::Nested(ref s) = field.val_type {
|
|
658
|
+
lookup_py_class(py, &s.name)
|
|
659
|
+
} else {
|
|
660
|
+
None
|
|
661
|
+
};
|
|
662
|
+
let f_meta = meta_opt.as_ref().and_then(|m| m.fields.get(i));
|
|
663
|
+
let is_init = f_meta.map(|f| f.is_init).unwrap_or(true);
|
|
664
|
+
let py_val = poly_value_to_py(
|
|
665
|
+
py,
|
|
666
|
+
field_val,
|
|
667
|
+
&field.val_type,
|
|
668
|
+
field_cls.as_ref(),
|
|
669
|
+
f_meta,
|
|
670
|
+
)?;
|
|
671
|
+
if let Some(meta) = f_meta {
|
|
672
|
+
if is_init {
|
|
673
|
+
kwargs.set_item(meta.py_string.bind(py), py_val)?;
|
|
601
674
|
} else {
|
|
602
|
-
|
|
603
|
-
break;
|
|
675
|
+
post_init_fields.push((meta.py_string.clone_ref(py), py_val));
|
|
604
676
|
}
|
|
677
|
+
} else if is_init {
|
|
678
|
+
kwargs.set_item(&field.name, py_val)?;
|
|
679
|
+
} else {
|
|
680
|
+
post_init_fields
|
|
681
|
+
.push((PyString::new(py, &field.name).unbind(), py_val));
|
|
605
682
|
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
let instance = target_cls.call((), Some(&kwargs))?;
|
|
686
|
+
for (attr_name, val) in post_init_fields {
|
|
687
|
+
instance.setattr(&attr_name, val)?;
|
|
688
|
+
}
|
|
689
|
+
return Ok(instance.unbind());
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
let dict = PyDict::new(py);
|
|
693
|
+
for (i, field) in rec_schema.fields.iter().enumerate() {
|
|
694
|
+
if let Some(Some(v)) = values.get(i) {
|
|
695
|
+
let py_v = poly_value_to_py(
|
|
696
|
+
py,
|
|
697
|
+
v,
|
|
698
|
+
&ValueType::Scalar(ScalarType::Any),
|
|
699
|
+
None,
|
|
700
|
+
None,
|
|
701
|
+
)?;
|
|
702
|
+
dict.set_item(&field.name, py_v)?;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
let instance = target_cls.call((), Some(&dict))?;
|
|
706
|
+
Ok(instance.unbind())
|
|
707
|
+
} else {
|
|
708
|
+
let dict = PyDict::new(py);
|
|
709
|
+
for (i, field) in rec_schema.fields.iter().enumerate() {
|
|
710
|
+
if let Some(Some(v)) = values.get(i) {
|
|
711
|
+
let py_v = poly_value_to_py(
|
|
712
|
+
py,
|
|
713
|
+
v,
|
|
714
|
+
&ValueType::Scalar(ScalarType::Any),
|
|
715
|
+
None,
|
|
716
|
+
None,
|
|
717
|
+
)?;
|
|
718
|
+
dict.set_item(&field.name, py_v)?;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
Ok(dict.into_any().unbind())
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
PolyValue::Object(map) => {
|
|
725
|
+
let meta_opt = if let ValueType::Nested(ref s) = val_type {
|
|
726
|
+
lookup_cached_meta(&s.name)
|
|
727
|
+
} else {
|
|
728
|
+
None
|
|
729
|
+
};
|
|
730
|
+
|
|
731
|
+
let effective_cls = match cls {
|
|
732
|
+
Some(c) => Some(c.clone()),
|
|
733
|
+
None => meta_opt.as_ref().map(|m| m.py_cls.bind(py).clone()),
|
|
734
|
+
};
|
|
735
|
+
|
|
736
|
+
if let Some(ref target_cls) = effective_cls {
|
|
737
|
+
if let ValueType::Nested(ref schema) = val_type {
|
|
738
|
+
// Method 1: Python 3.12+ Vectorcall for Dataclasses
|
|
739
|
+
if let Some(ref meta) = meta_opt {
|
|
740
|
+
if meta.is_dataclass && meta.all_init && !meta.fields.is_empty() {
|
|
741
|
+
if let Some(ref kwnames) = meta.kwnames {
|
|
742
|
+
let mut args_ptrs: SmallVec<[*mut pyo3::ffi::PyObject; 16]> =
|
|
743
|
+
SmallVec::with_capacity(schema.fields.len());
|
|
744
|
+
let mut py_vals: SmallVec<[PyObject; 16]> =
|
|
745
|
+
SmallVec::with_capacity(schema.fields.len());
|
|
746
|
+
let mut all_found = true;
|
|
747
|
+
|
|
748
|
+
for (i, field) in schema.fields.iter().enumerate() {
|
|
749
|
+
if let Some(field_val) = map.get(&field.name) {
|
|
750
|
+
let field_cls =
|
|
751
|
+
if let ValueType::Nested(ref s) = field.val_type {
|
|
752
|
+
lookup_py_class(py, &s.name)
|
|
753
|
+
} else {
|
|
754
|
+
None
|
|
755
|
+
};
|
|
756
|
+
let f_meta = meta.fields.get(i);
|
|
757
|
+
let py_val = poly_value_to_py(
|
|
758
|
+
py,
|
|
759
|
+
field_val,
|
|
760
|
+
&field.val_type,
|
|
761
|
+
field_cls.as_ref(),
|
|
762
|
+
f_meta,
|
|
763
|
+
)?;
|
|
764
|
+
args_ptrs.push(py_val.as_ptr());
|
|
765
|
+
py_vals.push(py_val);
|
|
766
|
+
} else {
|
|
767
|
+
all_found = false;
|
|
768
|
+
break;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
if all_found && args_ptrs.len() == schema.fields.len() {
|
|
773
|
+
unsafe {
|
|
774
|
+
let res_ptr = pyo3::ffi::PyObject_Vectorcall(
|
|
775
|
+
target_cls.as_ptr(),
|
|
776
|
+
args_ptrs.as_ptr(),
|
|
777
|
+
0,
|
|
778
|
+
kwnames.as_ptr(),
|
|
779
|
+
);
|
|
780
|
+
if !res_ptr.is_null() {
|
|
781
|
+
return Ok(Bound::from_owned_ptr(py, res_ptr).unbind());
|
|
782
|
+
} else {
|
|
783
|
+
return Err(PyErr::fetch(py));
|
|
784
|
+
}
|
|
785
|
+
}
|
|
610
786
|
}
|
|
611
787
|
}
|
|
612
788
|
}
|
|
@@ -679,18 +855,99 @@ fn py_to_poly_value<'py>(
|
|
|
679
855
|
schema: &ModelSchema,
|
|
680
856
|
meta_opt: Option<&CachedSchemaMeta>,
|
|
681
857
|
) -> PyResult<PolyValue> {
|
|
682
|
-
let
|
|
858
|
+
if let Some(meta) = meta_opt {
|
|
859
|
+
let mut values: Vec<Option<PolyValue>> = vec![None; schema.fields.len()];
|
|
683
860
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
if let Some(f_meta) = meta.fields.get(i) {
|
|
861
|
+
for (i, field) in schema.fields.iter().enumerate() {
|
|
862
|
+
let val_res = if let Some(f_meta) = meta.fields.get(i) {
|
|
687
863
|
obj.getattr(f_meta.py_string.bind(py))
|
|
688
864
|
} else {
|
|
689
865
|
obj.getattr(field.name.as_str())
|
|
866
|
+
};
|
|
867
|
+
|
|
868
|
+
if let Ok(val) = val_res {
|
|
869
|
+
if val.is_none() {
|
|
870
|
+
values[i] = Some(PolyValue::Null);
|
|
871
|
+
continue;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
match &field.val_type {
|
|
875
|
+
ValueType::Scalar(st) => match st {
|
|
876
|
+
ScalarType::Int => {
|
|
877
|
+
let int_val: i64 = if let Ok(enum_val) = val.getattr("value") {
|
|
878
|
+
enum_val.extract()?
|
|
879
|
+
} else {
|
|
880
|
+
val.extract()?
|
|
881
|
+
};
|
|
882
|
+
values[i] = Some(PolyValue::Int(int_val));
|
|
883
|
+
}
|
|
884
|
+
ScalarType::Float => {
|
|
885
|
+
let f: f64 = val.extract()?;
|
|
886
|
+
values[i] = Some(PolyValue::Float(f));
|
|
887
|
+
}
|
|
888
|
+
ScalarType::Bool => {
|
|
889
|
+
let b: bool = val.extract()?;
|
|
890
|
+
values[i] = Some(PolyValue::Bool(b));
|
|
891
|
+
}
|
|
892
|
+
_ => {
|
|
893
|
+
let s: String = if let Ok(enum_val) = val.getattr("value") {
|
|
894
|
+
enum_val.str()?.extract()?
|
|
895
|
+
} else {
|
|
896
|
+
val.str()?.extract()?
|
|
897
|
+
};
|
|
898
|
+
values[i] = Some(PolyValue::String(s));
|
|
899
|
+
}
|
|
900
|
+
},
|
|
901
|
+
ValueType::List(inner) => {
|
|
902
|
+
if let Ok(list) = val.cast::<PyList>() {
|
|
903
|
+
let mut poly_items = Vec::with_capacity(list.len());
|
|
904
|
+
let child_meta = if let ValueType::Nested(sub_schema) = inner.as_ref() {
|
|
905
|
+
lookup_cached_meta(&sub_schema.name)
|
|
906
|
+
} else {
|
|
907
|
+
None
|
|
908
|
+
};
|
|
909
|
+
for item in list.iter() {
|
|
910
|
+
if let ValueType::Nested(sub_schema) = inner.as_ref() {
|
|
911
|
+
poly_items.push(py_to_poly_value(
|
|
912
|
+
py,
|
|
913
|
+
&item,
|
|
914
|
+
sub_schema,
|
|
915
|
+
child_meta.as_deref(),
|
|
916
|
+
)?);
|
|
917
|
+
} else {
|
|
918
|
+
let s: String = if let Ok(enum_val) = item.getattr("value") {
|
|
919
|
+
enum_val.str()?.extract()?
|
|
920
|
+
} else {
|
|
921
|
+
item.str()?.extract()?
|
|
922
|
+
};
|
|
923
|
+
poly_items.push(PolyValue::String(s));
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
values[i] = Some(PolyValue::List(poly_items));
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
ValueType::Nested(sub_schema) => {
|
|
930
|
+
let child_meta = lookup_cached_meta(&sub_schema.name);
|
|
931
|
+
values[i] = Some(py_to_poly_value(
|
|
932
|
+
py,
|
|
933
|
+
&val,
|
|
934
|
+
sub_schema,
|
|
935
|
+
child_meta.as_deref(),
|
|
936
|
+
)?);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
690
939
|
}
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
|
|
940
|
+
}
|
|
941
|
+
return Ok(PolyValue::Record {
|
|
942
|
+
schema: Arc::clone(&meta.schema),
|
|
943
|
+
values: values.into_boxed_slice(),
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
let mut map = HashMap::with_capacity(schema.fields.len());
|
|
948
|
+
|
|
949
|
+
for field in &schema.fields {
|
|
950
|
+
let val_res = obj.getattr(field.name.as_str());
|
|
694
951
|
|
|
695
952
|
if let Ok(val) = val_res {
|
|
696
953
|
if val.is_none() {
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
use std::collections::HashMap;
|
|
2
|
-
|
|
3
|
-
#[derive(Debug, Clone, PartialEq)]
|
|
4
|
-
pub enum PolyValue {
|
|
5
|
-
Null,
|
|
6
|
-
Bool(bool),
|
|
7
|
-
Int(i64),
|
|
8
|
-
Float(f64),
|
|
9
|
-
String(String),
|
|
10
|
-
List(Vec<PolyValue>),
|
|
11
|
-
Object(HashMap<String, PolyValue>),
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
impl PolyValue {
|
|
15
|
-
pub fn is_null(&self) -> bool {
|
|
16
|
-
matches!(self, PolyValue::Null)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
pub fn as_str(&self) -> Option<&str> {
|
|
20
|
-
match self {
|
|
21
|
-
PolyValue::String(s) => Some(s.as_str()),
|
|
22
|
-
_ => None,
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
pub fn as_i64(&self) -> Option<i64> {
|
|
27
|
-
match self {
|
|
28
|
-
PolyValue::Int(i) => Some(*i),
|
|
29
|
-
_ => None,
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
pub fn as_f64(&self) -> Option<f64> {
|
|
34
|
-
match self {
|
|
35
|
-
PolyValue::Float(f) => Some(*f),
|
|
36
|
-
_ => None,
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
pub fn as_bool(&self) -> Option<bool> {
|
|
41
|
-
match self {
|
|
42
|
-
PolyValue::Bool(b) => Some(*b),
|
|
43
|
-
_ => None,
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
pub fn as_list(&self) -> Option<&[PolyValue]> {
|
|
48
|
-
match self {
|
|
49
|
-
PolyValue::List(l) => Some(l.as_slice()),
|
|
50
|
-
_ => None,
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
pub fn as_object(&self) -> Option<&HashMap<String, PolyValue>> {
|
|
55
|
-
match self {
|
|
56
|
-
PolyValue::Object(o) => Some(o),
|
|
57
|
-
_ => None,
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
pub fn get(&self, key: &str) -> Option<&PolyValue> {
|
|
62
|
-
match self {
|
|
63
|
-
PolyValue::Object(o) => o.get(key),
|
|
64
|
-
_ => None,
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/// Serialize this PolyValue into raw JSON bytes.
|
|
69
|
-
pub fn to_json(&self, indent: Option<usize>) -> crate::error::Result<Vec<u8>> {
|
|
70
|
-
let json_val = serde_json::Value::from(self);
|
|
71
|
-
match indent {
|
|
72
|
-
Some(spaces) if spaces > 0 => {
|
|
73
|
-
let indent_str = " ".repeat(spaces);
|
|
74
|
-
let formatter =
|
|
75
|
-
serde_json::ser::PrettyFormatter::with_indent(indent_str.as_bytes());
|
|
76
|
-
let mut buf = Vec::new();
|
|
77
|
-
let mut ser = serde_json::Serializer::with_formatter(&mut buf, formatter);
|
|
78
|
-
serde::Serialize::serialize(&json_val, &mut ser)?;
|
|
79
|
-
Ok(buf)
|
|
80
|
-
}
|
|
81
|
-
_ => Ok(serde_json::to_vec(&json_val)?),
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/// Deserialize raw JSON bytes into a dynamic PolyValue without a schema.
|
|
86
|
-
pub fn from_json(json_bytes: &[u8]) -> crate::error::Result<Self> {
|
|
87
|
-
let json_val: serde_json::Value = serde_json::from_slice(json_bytes)?;
|
|
88
|
-
Ok(PolyValue::from(json_val))
|
|
89
|
-
}
|
|
90
|
-
}
|
|
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
|