From 443577d48562fed9d3339e84c141129f6eaaf2c4 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Wed, 10 Oct 2018 20:17:14 -0600 Subject: [PATCH] Add support for i128 and u128 i128 and u128 can now be serialized and deserialized. Support is conditionalized on the disabled-by-default `i128` feature flag, and requires rustc 1.26.0 or later. The minimum required version of Serde is now 1.0.60. Fixes #108 --- .travis.yml | 4 + Cargo.toml | 6 +- src/de.rs | 44 ++++++ src/lib.rs | 12 ++ src/number.rs | 323 ++++++++++++++++++++++++++++++++++++++++++-- src/ser.rs | 30 ++++ src/value/de.rs | 68 ++++++++++ src/value/mod.rs | 119 +++++++++++++++- tests/test_de.rs | 5 +- tests/test_serde.rs | 48 +++++++ 10 files changed, 644 insertions(+), 15 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7a49f2f3..32800694 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,9 @@ matrix: - rust: 1.17.0 script: cargo check - rust: 1.21.0 + script: + - cargo build + - cargo test - rust: nightly env: CLIPPY script: | @@ -18,3 +21,4 @@ matrix: script: - cargo build - cargo test + - caro test --features "i128" diff --git a/Cargo.toml b/Cargo.toml index e1c698f0..1cdf03ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,9 +10,10 @@ readme = "README.md" keywords = ["yaml", "serde"] [dependencies] +cfg-if = "0.1" dtoa = "0.4" linked-hash-map = "0.5" -serde = "1.0" +serde = "1.0.60" yaml-rust = "0.4" [dev-dependencies] @@ -20,5 +21,8 @@ serde_derive = "1.0" unindent = "0.1" version-sync = "0.5" +[features] +i128 = [] + [badges] travis-ci = { repository = "dtolnay/serde-yaml" } diff --git a/src/de.rs b/src/de.rs index 8d6b1fe2..2fc1171d 100644 --- a/src/de.rs +++ b/src/de.rs @@ -565,9 +565,19 @@ where if let Ok(n) = v.parse() { return visitor.visit_u64(n); } + serde_if_integer128! { + if let Ok(n) = v.parse() { + return visitor.visit_u128(n); + } + } if let Ok(n) = v.parse() { return visitor.visit_i64(n); } + serde_if_integer128! { + if let Ok(n) = v.parse() { + return visitor.visit_i128(n); + } + } match v.trim_left_matches('+') { ".inf" | ".Inf" | ".INF" => return visitor.visit_f64(f64::INFINITY), _ => (), @@ -685,6 +695,23 @@ impl<'de, 'a, 'r> de::Deserializer<'de> for &'r mut Deserializer<'a> { self.deserialize_scalar(visitor) } + serde_if_integer128! { + #[cfg(feature = "i128")] + fn deserialize_i128(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_scalar(visitor) + } + #[cfg(not(feature = "i128"))] + fn deserialize_i128(self, _visitor: V) -> Result + where + V: Visitor<'de>, + { + Err(de::Error::custom("i128 is not supported. Enable the `i128` feature of `serde-yaml`")) + } + } + fn deserialize_u8(self, visitor: V) -> Result where V: Visitor<'de>, @@ -713,6 +740,23 @@ impl<'de, 'a, 'r> de::Deserializer<'de> for &'r mut Deserializer<'a> { self.deserialize_scalar(visitor) } + serde_if_integer128! { + #[cfg(feature = "i128")] + fn deserialize_u128(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_scalar(visitor) + } + #[cfg(not(feature = "i128"))] + fn deserialize_u128(self, _visitor: V) -> Result + where + V: Visitor<'de>, + { + Err(de::Error::custom("u128 is not supported. Enable the `i128` feature of `serde-yaml`")) + } + } + fn deserialize_f32(self, visitor: V) -> Result where V: Visitor<'de>, diff --git a/src/lib.rs b/src/lib.rs index ea114269..b4315eef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,6 +59,16 @@ //! assert_eq!(point, deserialized_point); //! # } //! ``` +//! +//! ## 128 bit numbers +//! +//! Support for `i128` and `u128` on Rust toolchains after `1.26.0` is enabled +//! through the `i128` feature. Add the following to `Cargo.toml`: +//! +//! ```toml,ignore +//! [dependencies.serde-yaml] +//! features = ["i128"] +//! ``` #![doc(html_root_url = "https://docs.rs/serde_yaml/0.8.5")] #![deny(missing_docs)] @@ -88,6 +98,8 @@ result_unwrap_used, ))] +#[macro_use] +extern crate cfg_if; extern crate dtoa; extern crate linked_hash_map; #[macro_use] diff --git a/src/number.rs b/src/number.rs index 7081ab57..3b262d3e 100644 --- a/src/number.rs +++ b/src/number.rs @@ -8,10 +8,13 @@ use error::Error; use serde::de::{Unexpected, Visitor}; +serde_if_integer128! { + use serde::de::Error as SError; +} use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt::{self, Debug, Display}; use std::hash::{Hash, Hasher}; -use std::i64; +use std::{i64, u64}; use std::mem; use private; @@ -32,6 +35,12 @@ enum N { NegInt(i64), /// May be infinite or NaN. Float(f64), + /// Always greater than u64::MAX + #[cfg(feature = "i128")] + PosInt128(u128), + /// Always less than i64::MIN + #[cfg(feature = "i128")] + NegInt128(i128) } impl Number { @@ -72,6 +81,60 @@ impl Number { N::PosInt(v) => v <= i64::max_value() as u64, N::NegInt(_) => true, N::Float(_) => false, + #[cfg(feature = "i128")] + N::PosInt128(v) => v <= i64::max_value() as u128, + #[cfg(feature = "i128")] + N::NegInt128(v) => v >= i64::min_value() as i128 + } + } + + /// Returns true if the `Number` is an integer between `i128::MIN` and + /// `i128::MAX`. + /// + /// For any Number on which `is_i128` returns true, `as_i128` is guaranteed + /// to return the integer value. + /// + /// ```rust + /// # #[macro_use] + /// # extern crate serde_yaml; + /// # + /// # fn yaml(i: &str) -> serde_yaml::Value { serde_yaml::from_str(i).unwrap() } + /// # fn main() { + /// let v = yaml(r#" + /// a: 64 + /// b: -64 + /// c: 256.0 + /// ## d does not fit in a u64 + /// d: 18446744073709551616 + /// ## e does not fit in an i64 + /// e: -9223372036854775809 + /// ## f does not fit in an i128 + /// f: 170141183460469231731687303715884105728 + /// "#); + /// + /// assert!(v["a"].is_i128()); + /// assert!(v["d"].is_i128()); + /// + /// // Negative integers. + /// assert!(v["b"].is_i128()); + /// assert!(v["e"].is_i128()); + /// + /// // Numbers with a decimal point are not considered integers. + /// assert!(!v["c"].is_i128()); + /// + /// // Integers that are too big for i128 + /// assert!(!v["f"].is_i128()); + /// # } + /// ``` + #[cfg(feature = "i128")] + #[inline] + pub fn is_i128(&self) -> bool { + match self.n { + N::PosInt(_) => true, + N::NegInt(_) => true, + N::Float(_) => false, + N::PosInt128(v) => v <= i128::max_value() as u128, + N::NegInt128(_) => true, } } @@ -105,7 +168,48 @@ impl Number { pub fn is_u64(&self) -> bool { match self.n { N::PosInt(_) => true, - N::NegInt(_) | N::Float(_) => false, + _ => false, + } + } + + /// Returns true if the `Number` is an integer between zero and `u128::MAX`. + /// + /// For any Number on which `is_u128` returns true, `as_u128` is guaranteed + /// to return the integer value. + /// + /// ```rust + /// # #[macro_use] + /// # extern crate serde_yaml; + /// # + /// # fn yaml(i: &str) -> serde_yaml::Value { serde_yaml::from_str(i).unwrap() } + /// # fn main() { + /// let v = yaml(r#" + /// a: 64 + /// b: -64 + /// c: 256.0 + /// ## d does not fit in a u64 + /// d: 18446744073709551616 + /// ## e does not fit in an i64 + /// e: -9223372036854775809 + /// "#); + /// + /// assert!(v["a"].is_u128()); + /// assert!(v["d"].is_u128()); + /// + /// // Negative integers. + /// assert!(!v["b"].is_u128()); + /// assert!(!v["e"].is_u128()); + /// + /// // Numbers with a decimal point are not considered integers. + /// assert!(!v["c"].is_u128()); + /// # } + /// ``` + #[cfg(feature = "i128")] + #[inline] + pub fn is_u128(&self) -> bool { + match self.n { + N::PosInt(_) | N::PosInt128(_) => true, + _ => false, } } @@ -141,7 +245,7 @@ impl Number { pub fn is_f64(&self) -> bool { match self.n { N::Float(_) => true, - N::PosInt(_) | N::NegInt(_) => false, + _ => false, } } @@ -177,7 +281,68 @@ impl Number { } else { None }, + #[cfg(feature = "i128")] + N::PosInt128(n) => if n <= i64::max_value() as u128 { + Some(n as i64) + } else { + None + }, N::NegInt(n) => Some(n), + #[cfg(feature = "i128")] + N::NegInt128(n) => if n >= i64::min_value() as i128 { + Some(n as i64) + } else { + None + }, + N::Float(_) => None, + } + } + + /// If the `Number` is an integer, represent it as i128 if possible. Returns + /// None otherwise. + /// + /// ```rust + /// # #[macro_use] + /// # extern crate serde_yaml; + /// # use std::i128; + /// # fn yaml(i: &str) -> serde_yaml::Value { serde_yaml::from_str(i).unwrap() } + /// # fn main() { + /// println!("i128::MAX is {:?}", i128::MAX); + /// let v = yaml(r#" + /// --- + /// c: 256.0 + /// ## d fits in an i64 + /// d: -9223372036854775808 + /// ## e does not fit in an i64 + /// e: -9223372036854775809 + /// ## f fits in a u64 + /// f: 18446744073709551615 + /// ## g does not fit in a u64 + /// g: 18446744073709551616 + /// ## h does not fit in an i128 + /// h: 170141183460469231731687303715884105728 + /// "#); + /// + /// assert_eq!(v["c"].as_i128(), None); + /// assert_eq!(v["d"].as_i128(), Some(-9223372036854775808)); + /// assert_eq!(v["e"].as_i128(), Some(-9223372036854775809)); + /// assert_eq!(v["f"].as_i128(), Some(18446744073709551615)); + /// assert_eq!(v["g"].as_i128(), Some(18446744073709551616)); + /// assert_eq!(v["h"].as_i128(), None); + /// # } + /// ``` + #[cfg(feature = "i128")] + #[inline] + pub fn as_i128(&self) -> Option { + match self.n { + N::PosInt(n) => Some(n as i128), + N::PosInt128(n) => if n <= i128::max_value() as u128 { + Some(n as i128) + } else { + None + }, + N::NegInt(n) => Some(n as i128), + N::NegInt128(n) => Some(n), N::Float(_) => None, } } @@ -207,7 +372,51 @@ impl Number { pub fn as_u64(&self) -> Option { match self.n { N::PosInt(n) => Some(n), - N::NegInt(_) | N::Float(_) => None, + #[cfg(feature = "i128")] + N::PosInt128(n) if n <= u64::MAX as u128 => Some(n as u64), + _ => None, + } + } + + /// If the `Number` is an integer, represent it as u128 if possible. Returns + /// None otherwise. + /// + /// ```rust + /// # #[macro_use] + /// # extern crate serde_yaml; + /// # + /// # fn yaml(i: &str) -> serde_yaml::Value { serde_yaml::from_str(i).unwrap() } + /// # fn main() { + /// let v = yaml(r#" + /// --- + /// c: 256.0 + /// ## d fits in an i64 + /// d: -9223372036854775808 + /// ## e does not fit in an i64 + /// e: -9223372036854775809 + /// ## f fits in a u64 + /// f: 18446744073709551615 + /// ## g does not fit in a u64 + /// g: 18446744073709551616 + /// ## h does not fit in an i128 + /// h: 170141183460469231731687303715884105728 + /// "#); + /// + /// assert_eq!(v["c"].as_u128(), None); + /// assert_eq!(v["d"].as_u128(), None); + /// assert_eq!(v["e"].as_u128(), None); + /// assert_eq!(v["f"].as_u128(), Some(18446744073709551615)); + /// assert_eq!(v["g"].as_u128(), Some(18446744073709551616)); + /// assert_eq!(v["h"].as_u128(), Some(170141183460469231731687303715884105728)); + /// # } + /// ``` + #[cfg(feature = "i128")] + #[inline] + pub fn as_u128(&self) -> Option { + match self.n { + N::PosInt(n) => Some(n as u128), + N::PosInt128(n) => Some(n), + _ => None, } } @@ -243,6 +452,10 @@ impl Number { pub fn as_f64(&self) -> Option { match self.n { N::PosInt(n) => Some(n as f64), + #[cfg(feature = "i128")] + N::PosInt128(n) => Some(n as f64), + #[cfg(feature = "i128")] + N::NegInt128(n) => Some(n as f64), N::NegInt(n) => Some(n as f64), N::Float(n) => Some(n), } @@ -268,8 +481,8 @@ impl Number { #[inline] pub fn is_nan(&self) -> bool { match self.n { - N::PosInt(_) | N::NegInt(_) => false, N::Float(f) => f.is_nan(), + _ => false, } } @@ -294,8 +507,8 @@ impl Number { #[inline] pub fn is_infinite(&self) -> bool { match self.n { - N::PosInt(_) | N::NegInt(_) => false, N::Float(f) => f.is_infinite(), + _ => false, } } @@ -319,8 +532,8 @@ impl Number { #[inline] pub fn is_finite(&self) -> bool { match self.n { - N::PosInt(_) | N::NegInt(_) => true, N::Float(f) => f.is_finite(), + _ => true, } } } @@ -329,6 +542,10 @@ impl fmt::Display for Number { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { match self.n { N::PosInt(i) => Display::fmt(&i, formatter), + #[cfg(feature = "i128")] + N::PosInt128(i) => Display::fmt(&i, formatter), + #[cfg(feature = "i128")] + N::NegInt128(i) => Display::fmt(&i, formatter), N::NegInt(i) => Display::fmt(&i, formatter), N::Float(f) if f.is_nan() => formatter.write_str(".nan"), N::Float(f) if f.is_infinite() => { @@ -353,6 +570,10 @@ impl PartialEq for N { fn eq(&self, other: &N) -> bool { match (*self, *other) { (N::PosInt(a), N::PosInt(b)) => a == b, + #[cfg(feature = "i128")] + (N::PosInt128(a), N::PosInt128(b)) => a == b, + #[cfg(feature = "i128")] + (N::NegInt128(a), N::NegInt128(b)) => a == b, (N::NegInt(a), N::NegInt(b)) => a == b, (N::Float(a), N::Float(b)) => { if a.is_nan() && b.is_nan() { @@ -379,6 +600,10 @@ impl Serialize for Number { { match self.n { N::PosInt(i) => serializer.serialize_u64(i), + #[cfg(feature = "i128")] + N::PosInt128(i) => serializer.serialize_u128(i), + #[cfg(feature = "i128")] + N::NegInt128(i) => serializer.serialize_i128(i), N::NegInt(i) => serializer.serialize_i64(i), N::Float(f) => serializer.serialize_f64(f), } @@ -405,11 +630,49 @@ impl<'de> Deserialize<'de> for Number { Ok(value.into()) } + serde_if_integer128! { + #[cfg(feature = "i128")] + #[inline] + fn visit_i128(self, value: i128) -> Result + where + E: SError, + { + Ok(value.into()) + } + #[cfg(not(feature = "i128"))] + #[inline] + fn visit_i128(self, _value: i128) -> Result + where + E: SError, + { + Err(SError::custom("i128 is not supported. Enable the `i128` feature of `serde-yaml`")) + } + } + #[inline] fn visit_u64(self, value: u64) -> Result { Ok(value.into()) } + serde_if_integer128! { + #[cfg(feature = "i128")] + #[inline] + fn visit_u128(self, value: u128) -> Result + where + E: SError, + { + Ok(value.into()) + } + #[cfg(not(feature = "i128"))] + #[inline] + fn visit_u128(self, _value: u128) -> Result + where + E: SError, + { + Err(SError::custom("u128 is not supported. Enable the `i128` feature of `serde-yaml`")) + } + } + #[inline] fn visit_f64(self, value: f64) -> Result { Ok(value.into()) @@ -430,13 +693,17 @@ impl<'de> Deserializer<'de> for Number { { match self.n { N::PosInt(i) => visitor.visit_u64(i), + #[cfg(feature = "i128")] + N::PosInt128(i) => visitor.visit_u128(i), + #[cfg(feature = "i128")] + N::NegInt128(i) => visitor.visit_i128(i), N::NegInt(i) => visitor.visit_i64(i), N::Float(f) => visitor.visit_f64(f), } } forward_to_deserialize_any! { - bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes + bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map struct enum identifier ignored_any } @@ -452,13 +719,17 @@ impl<'de, 'a> Deserializer<'de> for &'a Number { { match self.n { N::PosInt(i) => visitor.visit_u64(i), + #[cfg(feature = "i128")] + N::PosInt128(i) => visitor.visit_u128(i), + #[cfg(feature = "i128")] + N::NegInt128(i) => visitor.visit_i128(i), N::NegInt(i) => visitor.visit_i64(i), N::Float(f) => visitor.visit_f64(f), } } forward_to_deserialize_any! { - bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes + bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map struct enum identifier ignored_any } @@ -470,6 +741,19 @@ macro_rules! from_signed { impl From<$signed_ty> for Number { #[inline] #[cfg_attr(feature = "cargo-clippy", allow(cast_sign_loss))] + #[cfg(feature = "i128")] + fn from(i: $signed_ty) -> Self { + if i < 0 && i as i128 >= i64::MIN as i128 { + Number { n: N::NegInt(i as i64) } + } else if i < 0 { + Number { n: N::NegInt128(i as i128) } + } else if i as u128 <= u64::MAX as u128 { + Number { n: N::PosInt(i as u64) } + } else { + Number { n: N::PosInt128(i as u128) } + } + } + #[cfg(not(feature = "i128"))] fn from(i: $signed_ty) -> Self { if i < 0 { Number { n: N::NegInt(i as i64) } @@ -487,6 +771,15 @@ macro_rules! from_unsigned { $( impl From<$unsigned_ty> for Number { #[inline] + #[cfg(feature = "i128")] + fn from(u: $unsigned_ty) -> Self { + if u as u128 > u64::MAX as u128 { + Number { n: N::PosInt128(u as u128) } + } else { + Number { n: N::PosInt(u as u64) } + } + } + #[cfg(not(feature = "i128"))] fn from(u: $unsigned_ty) -> Self { Number { n: N::PosInt(u as u64) } } @@ -509,7 +802,11 @@ macro_rules! from_float { } from_signed!(i8 i16 i32 i64 isize); +#[cfg(feature = "i128")] +from_signed!(i128); from_unsigned!(u8 u16 u32 u64 usize); +#[cfg(feature = "i128")] +from_unsigned!(u128); from_float!(f32 f64); // This is fine, because we don't _really_ implement hash for floats @@ -523,7 +820,11 @@ impl Hash for Number { 3.hash(state) } N::PosInt(u) => u.hash(state), + #[cfg(feature = "i128")] + N::PosInt128(u) => u.hash(state), N::NegInt(i) => i.hash(state), + #[cfg(feature = "i128")] + N::NegInt128(i) => i.hash(state), } } } @@ -532,7 +833,11 @@ impl private { pub fn number_unexpected(number: &Number) -> Unexpected { match number.n { N::PosInt(u) => Unexpected::Unsigned(u), + #[cfg(feature = "i128")] + N::PosInt128(_) => unimplemented!(), N::NegInt(i) => Unexpected::Signed(i), + #[cfg(feature = "i128")] + N::NegInt128(_) => unimplemented!(), N::Float(f) => Unexpected::Float(f), } } diff --git a/src/ser.rs b/src/ser.rs index 61f10ed5..ba76edf5 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -53,6 +53,21 @@ impl ser::Serializer for Serializer { Ok(Yaml::Integer(v)) } + serde_if_integer128! { + #[cfg(feature = "i128")] + fn serialize_i128(self, v: i128) -> Result { + if v <= i64::max_value() as i128 && v >= i64::min_value() as i128 { + self.serialize_i64(v as i64) + } else { + Ok(Yaml::Real(v.to_string())) + } + } + #[cfg(not(feature = "i128"))] + fn serialize_i128(self, _v: i128) -> Result { + Err(ser::Error::custom("i128 is not supported. Enable the `i128` feature of `serde-yaml`")) + } + } + fn serialize_u8(self, v: u8) -> Result { self.serialize_i64(v as i64) } @@ -73,6 +88,21 @@ impl ser::Serializer for Serializer { } } + serde_if_integer128! { + #[cfg(feature = "i128")] + fn serialize_u128(self, v: u128) -> Result { + if v <= i64::max_value() as u128 { + self.serialize_i64(v as i64) + } else { + Ok(Yaml::Real(v.to_string())) + } + } + #[cfg(not(feature = "i128"))] + fn serialize_u128(self, _v: u128) -> Result { + Err(ser::Error::custom("u128 is not supported. Enable the `i128` feature of `serde-yaml`")) + } + } + fn serialize_f32(self, v: f32) -> Result { self.serialize_f64(v as f64) } diff --git a/src/value/de.rs b/src/value/de.rs index 53613ee4..18619293 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -36,6 +36,23 @@ impl<'de> Deserialize<'de> for Value { Ok(Value::Number(i.into())) } + serde_if_integer128! { + #[cfg(feature = "i128")] + fn visit_i128(self, i: i128) -> Result + where + E: SError, + { + Ok(Value::Number(i.into())) + } + #[cfg(not(feature = "i128"))] + fn visit_i128(self, _i: i128) -> Result + where + E: SError, + { + Err(SError::custom("i128 is not supported. Enable the `i128` feature of `serde-yaml`")) + } + } + fn visit_u64(self, u: u64) -> Result where E: SError, @@ -43,6 +60,23 @@ impl<'de> Deserialize<'de> for Value { Ok(Value::Number(u.into())) } + serde_if_integer128! { + #[cfg(feature = "i128")] + fn visit_u128(self, u: u128) -> Result + where + E: SError, + { + Ok(Value::Number(u.into())) + } + #[cfg(not(feature = "i128"))] + fn visit_u128(self, _i: u128) -> Result + where + E: SError, + { + Err(SError::custom("u128 is not supported. Enable the `i128` feature of `serde-yaml`")) + } + } + fn visit_f64(self, f: f64) -> Result where E: SError, @@ -213,6 +247,23 @@ impl<'de> Deserializer<'de> for Value { self.deserialize_number(visitor) } + serde_if_integer128! { + #[cfg(feature = "i128")] + fn deserialize_i128(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_number(visitor) + } + #[cfg(not(feature = "i128"))] + fn deserialize_i128(self, _visitor: V) -> Result + where + V: Visitor<'de>, + { + Err(SError::custom("i128 is not supported. Enable the `i128` feature of `serde-yaml`")) + } + } + fn deserialize_u8(self, visitor: V) -> Result where V: Visitor<'de>, @@ -241,6 +292,23 @@ impl<'de> Deserializer<'de> for Value { self.deserialize_number(visitor) } + serde_if_integer128! { + #[cfg(feature = "i128")] + fn deserialize_u128(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_number(visitor) + } + #[cfg(not(feature = "i128"))] + fn deserialize_u128(self, _visitor: V) -> Result + where + V: Visitor<'de>, + { + Err(SError::custom("u128 is not supported. Enable the `i128` feature of `serde-yaml`")) + } + } + fn deserialize_f32(self, visitor: V) -> Result where V: Visitor<'de>, diff --git a/src/value/mod.rs b/src/value/mod.rs index deb5c351..1650877e 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -8,6 +8,8 @@ use std::f64; use std::hash::{Hash, Hasher}; +#[cfg(feature = "i128")] +use std::num::ParseIntError; use std::str::FromStr; use serde::de::{Deserialize, DeserializeOwned}; @@ -269,6 +271,28 @@ impl Value { self.as_i64().is_some() } + /// Returns true if the `Value` is an integer between `i128::MIN` and + /// `i128::MAX`. + /// + /// For any Value on which `is_i128` returns true, `as_i128` is guaranteed to + /// return the integer value. + /// + /// ```rust + /// # use serde_yaml::Value; + /// let v: Value = serde_yaml::from_str("1337").unwrap(); + /// assert!(v.is_i128()); + /// ``` + /// + /// ```rust + /// # use serde_yaml::Value; + /// let v: Value = serde_yaml::from_str("null").unwrap(); + /// assert!(!v.is_i128()); + /// ``` + #[cfg(feature = "i128")] + pub fn is_i128(&self) -> bool { + self.as_i128().is_some() + } + /// If the `Value` is an integer, represent it as i64 if possible. Returns /// None otherwise. /// @@ -290,6 +314,28 @@ impl Value { } } + /// If the `Value` is an integer, represent it as i128 if possible. Returns + /// None otherwise. + /// + /// ```rust + /// # use serde_yaml::Value; + /// let v: Value = serde_yaml::from_str("1337").unwrap(); + /// assert_eq!(v.as_i128(), Some(1337)); + /// ``` + /// + /// ```rust + /// # use serde_yaml::Value; + /// let v: Value = serde_yaml::from_str("false").unwrap(); + /// assert_eq!(v.as_i128(), None); + /// ``` + #[cfg(feature = "i128")] + pub fn as_i128(&self) -> Option { + match *self { + Value::Number(ref n) => n.as_i128(), + _ => None + } + } + /// Returns true if the `Value` is an integer between `u64::MIN` and /// `u64::MAX`. /// @@ -311,6 +357,28 @@ impl Value { self.as_u64().is_some() } + /// Returns true if the `Value` is an integer between `u128::MIN` and + /// `u128::MAX`. + /// + /// For any Value on which `is_u128` returns true, `as_u128` is guaranteed to + /// return the integer value. + /// + /// ```rust + /// # use serde_yaml::Value; + /// let v: Value = serde_yaml::from_str("1337").unwrap(); + /// assert!(v.is_u128()); + /// ``` + /// + /// ```rust + /// # use serde_yaml::Value; + /// let v: Value = serde_yaml::from_str("null").unwrap(); + /// assert!(!v.is_u128()); + /// ``` + #[cfg(feature = "i128")] + pub fn is_u128(&self) -> bool { + self.as_u128().is_some() + } + /// If the `Value` is an integer, represent it as i64 if possible. Returns /// None otherwise. /// @@ -332,6 +400,28 @@ impl Value { } } + /// If the `Value` is an integer, represent it as u128 if possible. Returns + /// None otherwise. + /// + /// ```rust + /// # use serde_yaml::Value; + /// let v: Value = serde_yaml::from_str("1337").unwrap(); + /// assert_eq!(v.as_u128(), Some(1337)); + /// ``` + /// + /// ```rust + /// # use serde_yaml::Value; + /// let v: Value = serde_yaml::from_str("false").unwrap(); + /// assert_eq!(v.as_u128(), None); + /// ``` + #[cfg(feature = "i128")] + pub fn as_u128(&self) -> Option { + match *self { + Value::Number(ref n) => n.as_u128(), + _ => None + } + } + /// Returns true if the `Value` is a number that can be represented by f64. /// /// For any Value on which `is_f64` returns true, `as_f64` is guaranteed to @@ -565,10 +655,33 @@ fn yaml_to_value(yaml: Yaml) -> Value { Value::Number(n.into()) } else if let Ok(n) = i64::from_str(&f) { Value::Number(n.into()) - } else if let Ok(n) = f64::from_str(&f) { - Value::Number(n.into()) } else { - Value::String(f) + cfg_if! { + if #[cfg(feature = "i128")] { + fn u128_from_str(f: &str) -> Result { + u128::from_str(f) + } + fn i128_from_str(f: &str) -> Result { + i128::from_str(f) + } + } else { + fn u128_from_str(_f: &str) -> Result { + Err(()) + } + fn i128_from_str(_f: &str) -> Result { + Err(()) + } + } + } + if let Ok(n) = u128_from_str(&f) { + Value::Number(n.into()) + } else if let Ok(n) = i128_from_str(&f) { + Value::Number(n.into()) + } else if let Ok(n) = f64::from_str(&f) { + Value::Number(n.into()) + } else { + Value::String(f) + } } } Yaml::Integer(i) => Value::Number(i.into()), diff --git a/tests/test_de.rs b/tests/test_de.rs index 89b23d26..7b223df3 100644 --- a/tests/test_de.rs +++ b/tests/test_de.rs @@ -169,10 +169,11 @@ fn test_number_as_string() { let yaml = unindent( " --- - value: 123456789012345678901234567890", + # Cannot be represented as u128 + value: 340282366920938463463374607431768211457", ); let expected = Num { - value: "123456789012345678901234567890".to_owned(), + value: "340282366920938463463374607431768211457".to_owned(), }; test_de(&yaml, &expected); } diff --git a/tests/test_serde.rs b/tests/test_serde.rs index b36beb36..6b334522 100644 --- a/tests/test_serde.rs +++ b/tests/test_serde.rs @@ -91,6 +91,54 @@ fn test_int_max_i64() { test_serde(&thing, &yaml); } +#[cfg(feature = "i128")] +#[test] +fn test_i128_small() { + let thing: i128 = -256; + let yaml = unindent( + " + --- + -256", + ); + test_serde(&thing, &yaml); +} + +#[cfg(feature = "i128")] +#[test] +fn test_i128_big() { + let thing: i128 = ::std::i64::MIN as i128 - 1; + let yaml = unindent( + " + --- + -9223372036854775809", + ); + test_serde(&thing, &yaml); +} + +#[cfg(feature = "i128")] +#[test] +fn test_u128_small() { + let thing: u128 = 256; + let yaml = unindent( + " + --- + 256", + ); + test_serde(&thing, &yaml); +} + +#[cfg(feature = "i128")] +#[test] +fn test_u128_big() { + let thing: u128 = ::std::u64::MAX as u128 + 1; + let yaml = unindent( + " + --- + 18446744073709551616", + ); + test_serde(&thing, &yaml); +} + #[test] fn test_float() { let thing = 25.6;