|
1 | 1 | use std::marker::PhantomData; |
2 | 2 |
|
| 3 | +#[cfg(feature = "arbitrary_precision")] |
| 4 | +use pyo3::types::{PyAnyMethods, PyFloat, PyInt}; |
3 | 5 | use pyo3::types::{ |
4 | 6 | PyDict, PyDictMethods, PyList, PyListMethods, PyMapping, PySequence, PyString, PyTuple, |
5 | 7 | PyTupleMethods, |
@@ -229,6 +231,21 @@ pub struct PythonStructVariantSerializer<'py, P: PythonizeTypes> { |
229 | 231 | inner: PythonStructDictSerializer<'py, P>, |
230 | 232 | } |
231 | 233 |
|
| 234 | +#[cfg(feature = "arbitrary_precision")] |
| 235 | +#[doc(hidden)] |
| 236 | +pub enum StructSerializer<'py, P: PythonizeTypes> { |
| 237 | + Struct(PythonStructDictSerializer<'py, P>), |
| 238 | + Number { |
| 239 | + py: Python<'py>, |
| 240 | + number_string: Option<String>, |
| 241 | + _types: PhantomData<P>, |
| 242 | + }, |
| 243 | +} |
| 244 | + |
| 245 | +#[cfg(not(feature = "arbitrary_precision"))] |
| 246 | +#[doc(hidden)] |
| 247 | +pub type StructSerializer<'py, P> = PythonStructDictSerializer<'py, P>; |
| 248 | + |
232 | 249 | #[doc(hidden)] |
233 | 250 | pub struct PythonStructDictSerializer<'py, P: PythonizeTypes> { |
234 | 251 | py: Python<'py>, |
@@ -266,7 +283,7 @@ impl<'py, P: PythonizeTypes> ser::Serializer for Pythonizer<'py, P> { |
266 | 283 | type SerializeTupleStruct = PythonCollectionSerializer<'py, P>; |
267 | 284 | type SerializeTupleVariant = PythonTupleVariantSerializer<'py, P>; |
268 | 285 | type SerializeMap = PythonMapSerializer<'py, P>; |
269 | | - type SerializeStruct = PythonStructDictSerializer<'py, P>; |
| 286 | + type SerializeStruct = StructSerializer<'py, P>; |
270 | 287 | type SerializeStructVariant = PythonStructVariantSerializer<'py, P>; |
271 | 288 |
|
272 | 289 | fn serialize_bool(self, v: bool) -> Result<Bound<'py, PyAny>> { |
@@ -435,16 +452,34 @@ impl<'py, P: PythonizeTypes> ser::Serializer for Pythonizer<'py, P> { |
435 | 452 | }) |
436 | 453 | } |
437 | 454 |
|
438 | | - fn serialize_struct( |
439 | | - self, |
440 | | - name: &'static str, |
441 | | - len: usize, |
442 | | - ) -> Result<PythonStructDictSerializer<'py, P>> { |
443 | | - Ok(PythonStructDictSerializer { |
444 | | - py: self.py, |
445 | | - builder: P::NamedMap::builder(self.py, len, name)?, |
446 | | - _types: PhantomData, |
447 | | - }) |
| 455 | + fn serialize_struct(self, name: &'static str, len: usize) -> Result<StructSerializer<'py, P>> { |
| 456 | + #[cfg(feature = "arbitrary_precision")] |
| 457 | + { |
| 458 | + // With arbitrary_precision enabled, a serde_json::Number serializes as a "$serde_json::private::Number" |
| 459 | + // struct with a "$serde_json::private::Number" field, whose value is the String in Number::n. |
| 460 | + if name == "$serde_json::private::Number" && len == 1 { |
| 461 | + return Ok(StructSerializer::Number { |
| 462 | + py: self.py, |
| 463 | + number_string: None, |
| 464 | + _types: PhantomData, |
| 465 | + }); |
| 466 | + } |
| 467 | + |
| 468 | + Ok(StructSerializer::Struct(PythonStructDictSerializer { |
| 469 | + py: self.py, |
| 470 | + builder: P::NamedMap::builder(self.py, len, name)?, |
| 471 | + _types: PhantomData, |
| 472 | + })) |
| 473 | + } |
| 474 | + |
| 475 | + #[cfg(not(feature = "arbitrary_precision"))] |
| 476 | + { |
| 477 | + Ok(PythonStructDictSerializer { |
| 478 | + py: self.py, |
| 479 | + builder: P::NamedMap::builder(self.py, len, name)?, |
| 480 | + _types: PhantomData, |
| 481 | + }) |
| 482 | + } |
448 | 483 | } |
449 | 484 |
|
450 | 485 | fn serialize_struct_variant( |
@@ -569,6 +604,62 @@ impl<'py, P: PythonizeTypes> ser::SerializeMap for PythonMapSerializer<'py, P> { |
569 | 604 | } |
570 | 605 | } |
571 | 606 |
|
| 607 | +#[cfg(feature = "arbitrary_precision")] |
| 608 | +impl<'py, P: PythonizeTypes> ser::SerializeStruct for StructSerializer<'py, P> { |
| 609 | + type Ok = Bound<'py, PyAny>; |
| 610 | + type Error = PythonizeError; |
| 611 | + |
| 612 | + fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()> |
| 613 | + where |
| 614 | + T: ?Sized + Serialize, |
| 615 | + { |
| 616 | + match self { |
| 617 | + StructSerializer::Struct(s) => s.serialize_field(key, value), |
| 618 | + StructSerializer::Number { number_string, .. } => { |
| 619 | + let serde_json::Value::String(s) = value |
| 620 | + .serialize(serde_json::value::Serializer) |
| 621 | + .map_err(|e| { |
| 622 | + PythonizeError::msg(format!("Failed to serialize number: {}", e)) |
| 623 | + })? |
| 624 | + else { |
| 625 | + return Err(PythonizeError::msg("Expected string in serde_json::Number")); |
| 626 | + }; |
| 627 | + |
| 628 | + *number_string = Some(s); |
| 629 | + Ok(()) |
| 630 | + } |
| 631 | + } |
| 632 | + } |
| 633 | + |
| 634 | + fn end(self) -> Result<Bound<'py, PyAny>> { |
| 635 | + match self { |
| 636 | + StructSerializer::Struct(s) => s.end(), |
| 637 | + StructSerializer::Number { |
| 638 | + py, |
| 639 | + number_string: Some(s), |
| 640 | + .. |
| 641 | + } => { |
| 642 | + if let Ok(i) = s.parse::<i64>() { |
| 643 | + return Ok(PyInt::new(py, i).into_any()); |
| 644 | + } |
| 645 | + if let Ok(u) = s.parse::<u64>() { |
| 646 | + return Ok(PyInt::new(py, u).into_any()); |
| 647 | + } |
| 648 | + if s.chars().any(|c| c == '.' || c == 'e' || c == 'E') { |
| 649 | + if let Ok(f) = s.parse::<f64>() { |
| 650 | + return Ok(PyFloat::new(py, f).into_any()); |
| 651 | + } |
| 652 | + } |
| 653 | + // Fall back to Python's int() constructor, which supports arbitrary precision. |
| 654 | + py.get_type::<PyInt>() |
| 655 | + .call1((s.as_str(),)) |
| 656 | + .map_err(|e| PythonizeError::msg(format!("Invalid number: {}", e))) |
| 657 | + } |
| 658 | + StructSerializer::Number { .. } => Err(PythonizeError::msg("Empty serde_json::Number")), |
| 659 | + } |
| 660 | + } |
| 661 | +} |
| 662 | + |
572 | 663 | impl<'py, P: PythonizeTypes> ser::SerializeStruct for PythonStructDictSerializer<'py, P> { |
573 | 664 | type Ok = Bound<'py, PyAny>; |
574 | 665 | type Error = PythonizeError; |
|
0 commit comments