From d86703f33dc5a1569df8a164bc800b23e905a49f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 18 Oct 2024 08:26:12 -0700 Subject: [PATCH] Add Number u128 conversions --- src/number.rs | 78 ++++++++++++++++++++++++++++++++++++++++--------- src/value/de.rs | 8 +++++ 2 files changed, 72 insertions(+), 14 deletions(-) diff --git a/src/number.rs b/src/number.rs index 3982552fd..8cb0c3b7b 100644 --- a/src/number.rs +++ b/src/number.rs @@ -146,19 +146,6 @@ impl Number { self.n.parse().ok() } - /// If the `Number` is an integer, represent it as i128 if possible. Returns - /// None otherwise. - 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. pub fn as_u64(&self) -> Option { @@ -211,6 +198,31 @@ impl Number { } } + /// If the `Number` is an integer, represent it as i128 if possible. Returns + /// None otherwise. + 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 u128 if possible. Returns + /// None otherwise. + pub fn as_u128(&self) -> Option { + #[cfg(not(feature = "arbitrary_precision"))] + match self.n { + N::PosInt(n) => Some(n as u128), + N::NegInt(_) | N::Float(_) => None, + } + #[cfg(feature = "arbitrary_precision")] + self.n.parse().ok() + } + /// Converts an `i128` to a `Number`. Numbers smaller than i64::MIN or /// larger than u64::MAX can only be represented in `Number` if serde_json's /// "arbitrary_precision" feature is enabled. @@ -240,6 +252,33 @@ impl Number { Some(Number { n }) } + /// Converts a `u128` to a `Number`. Numbers greater than u64::MAX can only + /// be represented in `Number` if serde_json's "arbitrary_precision" feature + /// is enabled. + /// + /// ``` + /// # use serde_json::Number; + /// # + /// assert!(Number::from_u128(256).is_some()); + /// ``` + pub fn from_u128(i: u128) -> Option { + let n = { + #[cfg(not(feature = "arbitrary_precision"))] + { + if let Ok(u) = u64::try_from(i) { + N::PosInt(u) + } else { + return None; + } + } + #[cfg(feature = "arbitrary_precision")] + { + i.to_string() + } + }; + Some(Number { n }) + } + /// Returns the exact original JSON representation that this Number was /// parsed from. /// @@ -376,13 +415,22 @@ impl<'de> Deserialize<'de> for Number { where E: de::Error, { - Number::from_i128(value).ok_or_else(|| de::Error::custom("not a JSON number")) + Number::from_i128(value) + .ok_or_else(|| de::Error::custom("JSON number out of range")) } fn visit_u64(self, value: u64) -> Result { Ok(value.into()) } + fn visit_u128(self, value: u128) -> Result + where + E: de::Error, + { + Number::from_u128(value) + .ok_or_else(|| de::Error::custom("JSON number out of range")) + } + fn visit_f64(self, value: f64) -> Result where E: de::Error, @@ -503,6 +551,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(u) = self.as_u128() { + return visitor.visit_u128(u); } else if let Some(i) = self.as_i128() { return visitor.visit_i128(i); } else if let Some(f) = self.as_f64() { diff --git a/src/value/de.rs b/src/value/de.rs index 3973ae794..45891f033 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -57,6 +57,14 @@ impl<'de> Deserialize<'de> for Value { Ok(Value::Number(value.into())) } + fn visit_u128(self, value: u128) -> Result + where + E: serde::de::Error, + { + let de = serde::de::value::U128Deserializer::new(value); + Number::deserialize(de).map(Value::Number) + } + #[inline] fn visit_f64(self, value: f64) -> Result { Ok(Number::from_f64(value).map_or(Value::Null, Value::Number))