From 168ef4bb14fdd4a28787b1cb025d8f28f12eecb3 Mon Sep 17 00:00:00 2001 From: Ingvuil Date: Mon, 17 Jun 2024 22:40:22 +0100 Subject: [PATCH 1/2] Add i128 deserialization Implemented serde::de::Visitor::visit_i128. This fixes compatability with Ipld::Integer when using DagJsonCodec. --- src/number.rs | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ src/value/de.rs | 5 ++++ 2 files changed, 66 insertions(+) diff --git a/src/number.rs b/src/number.rs index 878a3dc..07f2bdf 100644 --- a/src/number.rs +++ b/src/number.rs @@ -202,6 +202,31 @@ impl Number { self.n.parse().ok() } + /// If the `Number` is an integer, represent it as i128 if possible. Returns + /// None otherwise. + /// + /// ``` + /// # use serde_json::json; + /// # + /// let big = i64::MAX as u64 + 10; + /// let v = json!({ "a": 64, "b": big, "c": 256.0 }); + /// + /// assert_eq!(v["a"].as_i128(), Some(64)); + /// assert_eq!(v["b"].as_i128(), None); + /// assert_eq!(v["c"].as_i128(), None); + /// ``` + #[inline] + pub fn as_i128(&self) -> Option { + #[cfg(not(feature = "arbitrary_precision"))] + match self.n { + N::PosInt(n) => Some(n as i128), + N::NegInt(n) => Some(n as i128), + N::Float(_) => None, + } + #[cfg(feature = "arbitrary_precision")] + self.n.parse().ok() + } + /// If the `Number` is an integer, represent it as u64 if possible. Returns /// None otherwise. /// @@ -279,6 +304,33 @@ impl Number { } } + /// Converts an `i128` to a `Number`. Greater than u64::MAX values are not JSON + /// numbers. + /// + /// ``` + /// # use std::i128; + /// # + /// # use serde_json::Number; + /// # + /// assert!(Number::from_i128(256).is_some()); + /// ``` + #[inline] + pub fn from_i128(i: i128) -> Option { + match u64::try_from(i) { + Ok(u) => Some(Number{ n: N::PosInt(u) }), + Err(_) => match i64::try_from(i) { + Ok(i) => { + if i >= 0 { + Some(Number{ n: N::PosInt(i as u64) }) + } else { + Some(Number{ n: N::NegInt(i) }) + } + }, + Err(_) => None + } + } + } + /// Returns the exact original JSON representation that this Number was /// parsed from. /// @@ -414,6 +466,13 @@ impl<'de> Deserialize<'de> for Number { Ok(value.into()) } + fn visit_i128(self, value: i128) -> Result + where + E: de::Error, + { + Number::from_i128(value).ok_or_else(|| de::Error::custom("not a JSON number")) + } + #[inline] fn visit_u64(self, value: u64) -> Result { Ok(value.into()) @@ -543,6 +602,8 @@ macro_rules! deserialize_any { return visitor.visit_u64(u); } else if let Some(i) = self.as_i64() { return visitor.visit_i64(i); + } else if let Some(i) = self.as_i128() { + return visitor.visit_i128(i); } else if let Some(f) = self.as_f64() { if ryu::Buffer::new().format_finite(f) == self.n || f.to_string() == self.n { return visitor.visit_f64(f); diff --git a/src/value/de.rs b/src/value/de.rs index 9367256..c38253a 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -44,6 +44,11 @@ impl<'de> Deserialize<'de> for Value { Ok(Value::Number(value.into())) } + #[inline] + fn visit_i128(self, value: i128) -> Result { + Ok(Number::from_i128(value).map_or(Value::Null, Value::Number)) + } + #[inline] fn visit_u64(self, value: u64) -> Result { Ok(Value::Number(value.into())) From 4f12af0ce0498e3b9eed0840487171bd06fac317 Mon Sep 17 00:00:00 2001 From: Ingvuil Date: Mon, 17 Jun 2024 23:20:01 +0100 Subject: [PATCH 2/2] Fix tests --- src/number.rs | 42 ++++++++++++++++++++++++++++++++++++------ src/value/mod.rs | 18 ++++++++++++++++++ 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/src/number.rs b/src/number.rs index 07f2bdf..1fccf2e 100644 --- a/src/number.rs +++ b/src/number.rs @@ -208,12 +208,10 @@ impl Number { /// ``` /// # use serde_json::json; /// # - /// let big = i64::MAX as u64 + 10; - /// let v = json!({ "a": 64, "b": big, "c": 256.0 }); + /// let v = json!({ "a": 64, "b": 256.0 }); /// /// assert_eq!(v["a"].as_i128(), Some(64)); /// assert_eq!(v["b"].as_i128(), None); - /// assert_eq!(v["c"].as_i128(), None); /// ``` #[inline] pub fn as_i128(&self) -> Option { @@ -317,13 +315,45 @@ impl Number { #[inline] pub fn from_i128(i: i128) -> Option { match u64::try_from(i) { - Ok(u) => Some(Number{ n: N::PosInt(u) }), + Ok(u) => { + let n = { + #[cfg(not(feature = "arbitrary_precision"))] + { + N::PosInt(u) + } + #[cfg(feature = "arbitrary_precision")] + { + u.to_string() + } + }; + Some(Number{ n }) + }, Err(_) => match i64::try_from(i) { Ok(i) => { if i >= 0 { - Some(Number{ n: N::PosInt(i as u64) }) + let n = { + #[cfg(not(feature = "arbitrary_precision"))] + { + N::PosInt(i as u64) + } + #[cfg(feature = "arbitrary_precision")] + { + i.to_string() + } + }; + Some(Number{ n }) } else { - Some(Number{ n: N::NegInt(i) }) + let n = { + #[cfg(not(feature = "arbitrary_precision"))] + { + N::NegInt(i) + } + #[cfg(feature = "arbitrary_precision")] + { + i.to_string() + } + }; + Some(Number{ n }) } }, Err(_) => None diff --git a/src/value/mod.rs b/src/value/mod.rs index b3f51ea..4f246ab 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -635,6 +635,24 @@ impl Value { } } + /// If the `Value` is an integer, represent it as i128 if possible. Returns + /// None otherwise. + /// + /// ``` + /// # use serde_json::json; + /// # + /// let v = json!({ "a": 64, "b": 256.0 }); + /// + /// assert_eq!(v["a"].as_i128(), Some(64)); + /// assert_eq!(v["b"].as_i128(), None); + /// ``` + pub fn as_i128(&self) -> Option { + match self { + Value::Number(n) => n.as_i128(), + _ => None, + } + } + /// If the `Value` is an integer, represent it as u64 if possible. Returns /// None otherwise. ///