From 9f5b68549a0b6fd630df14e4f061dd69b4e19de7 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 23 Nov 2022 16:13:57 +0800 Subject: [PATCH] Deserialize large numbers. --- CHANGELOG.md | 5 + src/serde/de.rs | 85 ++++++++-------- src/serde/deserialize.rs | 130 ++++++++++++++++-------- src/serde/ser.rs | 206 ++++++++++++++++++++------------------- src/serde/serialize.rs | 2 +- tests/serde.rs | 31 +++++- 6 files changed, 273 insertions(+), 186 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04b4baa9..58425903 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ Rhai Release Notes Version 1.12.0 ============== +Buf fixes +--------- + +* Integer numbers that are too large to deserialize into `INT` now fall back to `Decimal` or `FLOAT` instead of silently truncating. + Net features ------------ diff --git a/src/serde/de.rs b/src/serde/de.rs index 1e3f3664..2cf9ec3f 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -47,7 +47,7 @@ impl<'de> DynamicDeserializer<'de> { ) } #[inline(always)] - fn deserialize_int>(self, v: crate::INT, visitor: V) -> RhaiResultOf { + fn deserialize_int>(v: crate::INT, visitor: V) -> RhaiResultOf { #[cfg(not(feature = "only_i32"))] return visitor.visit_i64(v); #[cfg(feature = "only_i32")] @@ -185,7 +185,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { fn deserialize_i8>(self, visitor: V) -> RhaiResultOf { if let Ok(v) = self.0.as_int() { - self.deserialize_int(v, visitor) + Self::deserialize_int(v, visitor) } else { self.0 .downcast_ref::() @@ -195,7 +195,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { fn deserialize_i16>(self, visitor: V) -> RhaiResultOf { if let Ok(v) = self.0.as_int() { - self.deserialize_int(v, visitor) + Self::deserialize_int(v, visitor) } else { self.0 .downcast_ref::() @@ -205,7 +205,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { fn deserialize_i32>(self, visitor: V) -> RhaiResultOf { if let Ok(v) = self.0.as_int() { - self.deserialize_int(v, visitor) + Self::deserialize_int(v, visitor) } else if cfg!(feature = "only_i32") { self.type_error() } else { @@ -217,7 +217,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { fn deserialize_i64>(self, visitor: V) -> RhaiResultOf { if let Ok(v) = self.0.as_int() { - self.deserialize_int(v, visitor) + Self::deserialize_int(v, visitor) } else if cfg!(not(feature = "only_i32")) { self.type_error() } else { @@ -229,7 +229,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { fn deserialize_i128>(self, visitor: V) -> RhaiResultOf { if let Ok(v) = self.0.as_int() { - self.deserialize_int(v, visitor) + Self::deserialize_int(v, visitor) } else if cfg!(not(feature = "only_i32")) { self.type_error() } else { @@ -241,7 +241,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { fn deserialize_u8>(self, visitor: V) -> RhaiResultOf { if let Ok(v) = self.0.as_int() { - self.deserialize_int(v, visitor) + Self::deserialize_int(v, visitor) } else { self.0 .downcast_ref::() @@ -251,7 +251,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { fn deserialize_u16>(self, visitor: V) -> RhaiResultOf { if let Ok(v) = self.0.as_int() { - self.deserialize_int(v, visitor) + Self::deserialize_int(v, visitor) } else { self.0 .downcast_ref::() @@ -261,7 +261,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { fn deserialize_u32>(self, visitor: V) -> RhaiResultOf { if let Ok(v) = self.0.as_int() { - self.deserialize_int(v, visitor) + Self::deserialize_int(v, visitor) } else { self.0 .downcast_ref::() @@ -271,7 +271,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { fn deserialize_u64>(self, visitor: V) -> RhaiResultOf { if let Ok(v) = self.0.as_int() { - self.deserialize_int(v, visitor) + Self::deserialize_int(v, visitor) } else { self.0 .downcast_ref::() @@ -281,7 +281,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { fn deserialize_u128>(self, visitor: V) -> RhaiResultOf { if let Ok(v) = self.0.as_int() { - self.deserialize_int(v, visitor) + Self::deserialize_int(v, visitor) } else { self.0 .downcast_ref::() @@ -296,21 +296,21 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { .downcast_ref::() .map_or_else(|| self.type_error(), |&x| _visitor.visit_f32(x)); - #[cfg(feature = "no_float")] - #[cfg(feature = "decimal")] + #[allow(unreachable_code)] { - use rust_decimal::prelude::ToPrimitive; + #[cfg(feature = "decimal")] + { + use rust_decimal::prelude::ToPrimitive; - return self - .0 - .downcast_ref::() - .and_then(|&x| x.to_f32()) - .map_or_else(|| self.type_error(), |v| _visitor.visit_f32(v)); + return self + .0 + .downcast_ref::() + .and_then(|&x| x.to_f32()) + .map_or_else(|| self.type_error(), |v| _visitor.visit_f32(v)); + } + + self.type_error_str("f32") } - - #[cfg(feature = "no_float")] - #[cfg(not(feature = "decimal"))] - return self.type_error_str("f32"); } fn deserialize_f64>(self, _visitor: V) -> RhaiResultOf { @@ -320,21 +320,21 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { .downcast_ref::() .map_or_else(|| self.type_error(), |&x| _visitor.visit_f64(x)); - #[cfg(feature = "no_float")] - #[cfg(feature = "decimal")] + #[allow(unreachable_code)] { - use rust_decimal::prelude::ToPrimitive; + #[cfg(feature = "decimal")] + { + use rust_decimal::prelude::ToPrimitive; - return self - .0 - .downcast_ref::() - .and_then(|&x| x.to_f64()) - .map_or_else(|| self.type_error(), |v| _visitor.visit_f64(v)); + return self + .0 + .downcast_ref::() + .and_then(|&x| x.to_f64()) + .map_or_else(|| self.type_error(), |v| _visitor.visit_f64(v)); + } + + self.type_error_str("f64") } - - #[cfg(feature = "no_float")] - #[cfg(not(feature = "decimal"))] - return self.type_error_str("f64"); } fn deserialize_char>(self, visitor: V) -> RhaiResultOf { @@ -517,10 +517,9 @@ impl<'de, ITER: Iterator> serde::de::SeqAccess<'de> seed: T, ) -> RhaiResultOf> { // Deserialize each item coming out of the iterator. - match self.iter.next() { - Some(item) => seed.deserialize(item.into_deserializer()).map(Some), - None => Ok(None), - } + self.iter.next().map_or(Ok(None), |item| { + seed.deserialize(item.into_deserializer()).map(Some) + }) } } @@ -553,10 +552,10 @@ impl<'de, K: Iterator, V: Iterator> serde: seed: S, ) -> RhaiResultOf> { // Deserialize each `Identifier` key coming out of the keys iterator. - match self.keys.next().map(<_>::into_deserializer) { - Some(d) => seed.deserialize(d).map(Some), - None => Ok(None), - } + self.keys + .next() + .map(<_>::into_deserializer) + .map_or(Ok(None), |d| seed.deserialize(d).map(Some)) } fn next_value_seed>( diff --git a/src/serde/deserialize.rs b/src/serde/deserialize.rs index d16ef71f..680632c6 100644 --- a/src/serde/deserialize.rs +++ b/src/serde/deserialize.rs @@ -1,6 +1,7 @@ //! Implementations of [`serde::Deserialize`]. use crate::{Dynamic, Identifier, ImmutableString, Scope, INT}; +use num_traits::FromPrimitive; use serde::{ de::{Error, SeqAccess, Visitor}, Deserialize, Deserializer, @@ -38,14 +39,43 @@ impl<'de> Visitor<'de> for DynamicVisitor { #[inline] fn visit_i64(self, v: i64) -> Result { #[cfg(not(feature = "only_i32"))] - { - Ok(v.into()) - } + return Ok(v.into()); + #[cfg(feature = "only_i32")] - if v > i32::MAX as i64 { - Ok(Dynamic::from(v)) - } else { - self.visit_i32(v as i32) + if v <= INT::MAX as i64 { + return Ok(Dynamic::from(v as INT)); + } + + #[allow(unreachable_code)] + { + #[cfg(feature = "decimal")] + if let Some(n) = rust_decimal::Decimal::from_i64(v) { + return Ok(Dynamic::from_decimal(n)); + } + + #[cfg(not(feature = "no_float"))] + return Ok(Dynamic::from_float(v as crate::FLOAT)); + + Err(Error::custom(format!("integer number too large: {}", v))) + } + } + #[inline] + fn visit_i128(self, v: i128) -> Result { + if v <= i128::from(INT::MAX) { + return Ok(Dynamic::from(v as INT)); + } + + #[allow(unreachable_code)] + { + #[cfg(feature = "decimal")] + if let Some(n) = rust_decimal::Decimal::from_i128(v) { + return Ok(Dynamic::from_decimal(n)); + } + + #[cfg(not(feature = "no_float"))] + return Ok(Dynamic::from_float(v as crate::FLOAT)); + + Err(Error::custom(format!("integer number too large: {}", v))) } } #[inline(always)] @@ -59,57 +89,79 @@ impl<'de> Visitor<'de> for DynamicVisitor { #[inline] fn visit_u32(self, v: u32) -> Result { #[cfg(not(feature = "only_i32"))] - { - Ok(INT::from(v).into()) - } + return Ok(Dynamic::from(v as INT)); + #[cfg(feature = "only_i32")] - if v > i32::MAX as u32 { - Ok(Dynamic::from(v)) - } else { - self.visit_i32(v as i32) + if v <= INT::MAX as i32 { + return Ok(Dynamic::from(v as INT)); + } + + #[allow(unreachable_code)] + { + #[cfg(feature = "decimal")] + if let Some(n) = rust_decimal::Decimal::from_u32(v) { + return Ok(Dynamic::from_decimal(n)); + } + + #[cfg(not(feature = "no_float"))] + return Ok(Dynamic::from_float(v as crate::FLOAT)); + + Err(Error::custom(format!("integer number too large: {}", v))) } } #[inline] fn visit_u64(self, v: u64) -> Result { - #[cfg(not(feature = "only_i32"))] - if v > i64::MAX as u64 { - Ok(Dynamic::from(v)) - } else { - self.visit_i64(v as i64) + if v <= INT::MAX as u64 { + return Ok(Dynamic::from(v as INT)); } - #[cfg(feature = "only_i32")] - if v > i32::MAX as u64 { - Ok(Dynamic::from(v)) - } else { - self.visit_i32(v as i32) + + #[cfg(feature = "decimal")] + if let Some(n) = rust_decimal::Decimal::from_u64(v) { + return Ok(Dynamic::from_decimal(n)); } + + #[cfg(not(feature = "no_float"))] + return Ok(Dynamic::from_float(v as crate::FLOAT)); + + #[allow(unreachable_code)] + Err(Error::custom(format!("integer number too large: {}", v))) + } + #[inline] + fn visit_u128(self, v: u128) -> Result { + if v <= INT::MAX as u128 { + return Ok(Dynamic::from(v as INT)); + } + + #[cfg(feature = "decimal")] + if let Some(n) = rust_decimal::Decimal::from_u128(v) { + return Ok(Dynamic::from_decimal(n)); + } + + #[cfg(not(feature = "no_float"))] + return Ok(Dynamic::from_float(v as crate::FLOAT)); + + #[allow(unreachable_code)] + Err(Error::custom(format!("integer number too large: {}", v))) } #[cfg(not(feature = "no_float"))] #[inline(always)] fn visit_f32(self, v: f32) -> Result { - #[cfg(not(feature = "f32_float"))] - return self.visit_f64(v as f64); - #[cfg(feature = "f32_float")] - return Ok(v.into()); + Ok(crate::FLOAT::from(v).into()) } #[cfg(not(feature = "no_float"))] #[inline(always)] fn visit_f64(self, v: f64) -> Result { - #[cfg(not(feature = "f32_float"))] - return Ok(v.into()); - #[cfg(feature = "f32_float")] - return self.visit_f32(v as f32); + Ok(crate::FLOAT::from(v).into()) } #[cfg(feature = "no_float")] #[cfg(feature = "decimal")] #[inline] fn visit_f32(self, v: f32) -> Result { - use rust_decimal::Decimal; use std::convert::TryFrom; - Decimal::try_from(v) + rust_decimal::Decimal::try_from(v) .map(|v| v.into()) .map_err(Error::custom) } @@ -117,10 +169,9 @@ impl<'de> Visitor<'de> for DynamicVisitor { #[cfg(feature = "decimal")] #[inline] fn visit_f64(self, v: f64) -> Result { - use rust_decimal::Decimal; use std::convert::TryFrom; - Decimal::try_from(v) + rust_decimal::Decimal::try_from(v) .map(|v| v.into()) .map_err(Error::custom) } @@ -216,10 +267,9 @@ impl<'de> Deserialize<'de> for Scope<'de> { where A: SeqAccess<'de>, { - let mut scope = match access.size_hint() { - Some(size) => Scope::with_capacity(size), - None => Scope::new(), - }; + let mut scope = access + .size_hint() + .map_or_else(Scope::new, Scope::with_capacity); while let Some(ScopeEntry { name, diff --git a/src/serde/ser.rs b/src/serde/ser.rs index 4104fd19..a6e7da68 100644 --- a/src/serde/ser.rs +++ b/src/serde/ser.rs @@ -1,6 +1,7 @@ //! Implement serialization support of [`Dynamic`][crate::Dynamic] for [`serde`]. -use crate::{Dynamic, Identifier, Position, RhaiError, RhaiResult, RhaiResultOf, ERR}; +use crate::{Dynamic, Identifier, Position, RhaiError, RhaiResult, RhaiResultOf, ERR, INT}; +use num_traits::FromPrimitive; use serde::ser::{ Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct, }; @@ -112,152 +113,164 @@ impl Serializer for &mut DynamicSerializer { #[inline(always)] fn serialize_i8(self, v: i8) -> RhaiResultOf { - #[cfg(not(feature = "only_i32"))] - return self.serialize_i64(i64::from(v)); - #[cfg(feature = "only_i32")] - return self.serialize_i32(i32::from(v)); + Ok(INT::from(v).into()) } #[inline(always)] fn serialize_i16(self, v: i16) -> RhaiResultOf { - #[cfg(not(feature = "only_i32"))] - return self.serialize_i64(i64::from(v)); - #[cfg(feature = "only_i32")] - return self.serialize_i32(i32::from(v)); + Ok(INT::from(v).into()) } #[inline(always)] fn serialize_i32(self, v: i32) -> RhaiResultOf { - #[cfg(not(feature = "only_i32"))] - return self.serialize_i64(i64::from(v)); - #[cfg(feature = "only_i32")] - return Ok(v.into()); + Ok(INT::from(v).into()) } #[inline] fn serialize_i64(self, v: i64) -> RhaiResultOf { #[cfg(not(feature = "only_i32"))] - { - Ok(v.into()) - } + return Ok(v.into()); + #[cfg(feature = "only_i32")] - if v > i32::MAX as i64 { - Ok(Dynamic::from(v)) - } else { - self.serialize_i32(v as i32) + if v <= INT::MAX as i64 { + return Ok(Dynamic::from(v as INT)); + } + + #[allow(unreachable_code)] + { + #[cfg(feature = "decimal")] + if let Some(n) = rust_decimal::Decimal::from_i64(v) { + return Ok(Dynamic::from_decimal(n)); + } + + #[cfg(not(feature = "no_float"))] + return Ok(Dynamic::from_float(v as crate::FLOAT)); + + Err(Error::custom(format!("integer number too large: {}", v))) } } #[inline] fn serialize_i128(self, v: i128) -> RhaiResultOf { - #[cfg(not(feature = "only_i32"))] - if v > i64::MAX as i128 { - Ok(Dynamic::from(v)) - } else { - self.serialize_i64(v as i64) + if v <= i128::from(INT::MAX) { + return Ok(Dynamic::from(v as INT)); } - #[cfg(feature = "only_i32")] - if v > i32::MAX as i128 { - Ok(Dynamic::from(v)) - } else { - self.serialize_i32(v as i32) + + #[allow(unreachable_code)] + { + #[cfg(feature = "decimal")] + if let Some(n) = rust_decimal::Decimal::from_i128(v) { + return Ok(Dynamic::from_decimal(n)); + } + + #[cfg(not(feature = "no_float"))] + return Ok(Dynamic::from_float(v as crate::FLOAT)); + + Err(Error::custom(format!("integer number too large: {}", v))) } } #[inline(always)] fn serialize_u8(self, v: u8) -> RhaiResultOf { - #[cfg(not(feature = "only_i32"))] - return self.serialize_i64(i64::from(v)); - #[cfg(feature = "only_i32")] - return self.serialize_i32(i32::from(v)); + Ok(INT::from(v).into()) } #[inline(always)] fn serialize_u16(self, v: u16) -> RhaiResultOf { - #[cfg(not(feature = "only_i32"))] - return self.serialize_i64(i64::from(v)); - #[cfg(feature = "only_i32")] - return self.serialize_i32(i32::from(v)); + Ok(INT::from(v).into()) } #[inline] fn serialize_u32(self, v: u32) -> RhaiResultOf { #[cfg(not(feature = "only_i32"))] - { - self.serialize_i64(i64::from(v)) - } + return Ok(Dynamic::from(v as INT)); + #[cfg(feature = "only_i32")] - if v > i32::MAX as u32 { - Ok(Dynamic::from(v)) - } else { - self.serialize_i32(v as i32) + if v <= INT::MAX as i32 { + return Ok(Dynamic::from(v as INT)); + } + + #[allow(unreachable_code)] + { + #[cfg(feature = "decimal")] + if let Some(n) = rust_decimal::Decimal::from_u32(v) { + return Ok(Dynamic::from_decimal(n)); + } + + #[cfg(not(feature = "no_float"))] + return Ok(Dynamic::from_float(v as crate::FLOAT)); + + Err(Error::custom(format!("integer number too large: {}", v))) } } #[inline] fn serialize_u64(self, v: u64) -> RhaiResultOf { - #[cfg(not(feature = "only_i32"))] - if v > i64::MAX as u64 { - Ok(Dynamic::from(v)) - } else { - self.serialize_i64(v as i64) + if v <= INT::MAX as u64 { + return Ok(Dynamic::from(v as INT)); } - #[cfg(feature = "only_i32")] - if v > i32::MAX as u64 { - Ok(Dynamic::from(v)) - } else { - self.serialize_i32(v as i32) + + #[cfg(feature = "decimal")] + if let Some(n) = rust_decimal::Decimal::from_u64(v) { + return Ok(Dynamic::from_decimal(n)); } + + #[cfg(not(feature = "no_float"))] + return Ok(Dynamic::from_float(v as crate::FLOAT)); + + #[allow(unreachable_code)] + Err(Error::custom(format!("integer number too large: {}", v))) } #[inline] fn serialize_u128(self, v: u128) -> RhaiResultOf { - #[cfg(not(feature = "only_i32"))] - if v > i64::MAX as u128 { - Ok(Dynamic::from(v)) - } else { - self.serialize_i64(v as i64) + if v <= INT::MAX as u128 { + return Ok(Dynamic::from(v as INT)); } - #[cfg(feature = "only_i32")] - if v > i32::MAX as u128 { - Ok(Dynamic::from(v)) - } else { - self.serialize_i32(v as i32) + + #[cfg(feature = "decimal")] + if let Some(n) = rust_decimal::Decimal::from_u128(v) { + return Ok(Dynamic::from_decimal(n)); } + + #[cfg(not(feature = "no_float"))] + return Ok(Dynamic::from_float(v as crate::FLOAT)); + + #[allow(unreachable_code)] + Err(Error::custom(format!("integer number too large: {}", v))) } + #[cfg(not(feature = "no_float"))] + #[inline(always)] + fn serialize_f32(self, v: f32) -> RhaiResultOf { + Ok(crate::FLOAT::from(v).into()) + } + + #[cfg(not(feature = "no_float"))] + #[inline(always)] + fn serialize_f64(self, v: f64) -> RhaiResultOf { + Ok(crate::FLOAT::from(v).into()) + } + + #[cfg(feature = "no_float")] + #[cfg(feature = "decimal")] #[inline] fn serialize_f32(self, v: f32) -> RhaiResultOf { - #[cfg(any(not(feature = "no_float"), not(feature = "decimal")))] - return Ok(Dynamic::from(v)); + use std::convert::TryFrom; - #[cfg(feature = "no_float")] - #[cfg(feature = "decimal")] - { - use rust_decimal::Decimal; - use std::convert::TryFrom; - - Decimal::try_from(v) - .map(|v| v.into()) - .map_err(Error::custom) - } + rust_decimal::Decimal::try_from(v) + .map(|v| v.into()) + .map_err(Error::custom) } - + #[cfg(feature = "no_float")] + #[cfg(feature = "decimal")] #[inline] fn serialize_f64(self, v: f64) -> RhaiResultOf { - #[cfg(any(not(feature = "no_float"), not(feature = "decimal")))] - return Ok(Dynamic::from(v)); + use std::convert::TryFrom; - #[cfg(feature = "no_float")] - #[cfg(feature = "decimal")] - { - use rust_decimal::Decimal; - use std::convert::TryFrom; - - Decimal::try_from(v) - .map(|v| v.into()) - .map_err(Error::custom) - } + rust_decimal::Decimal::try_from(v) + .map(|v| v.into()) + .map_err(Error::custom) } #[inline(always)] @@ -332,10 +345,7 @@ impl Serializer for &mut DynamicSerializer { _value: &T, ) -> RhaiResultOf { #[cfg(not(feature = "no_object"))] - { - let content = to_dynamic(_value)?; - make_variant(_variant, content) - } + return Ok(make_variant(_variant, to_dynamic(_value)?)); #[cfg(feature = "no_object")] return Err(ERR::ErrorMismatchDataType( "".into(), @@ -688,7 +698,7 @@ impl serde::ser::SerializeTupleVariant for TupleVariantSerializer { #[inline] fn end(self) -> RhaiResultOf { - make_variant(self.variant, self.array.into()) + Ok(make_variant(self.variant, self.array.into())) } } @@ -716,14 +726,14 @@ impl serde::ser::SerializeStructVariant for StructVariantSerializer { #[inline] fn end(self) -> RhaiResultOf { - make_variant(self.variant, self.map.into()) + Ok(make_variant(self.variant, self.map.into())) } } #[cfg(not(feature = "no_object"))] #[inline] -fn make_variant(variant: &'static str, value: Dynamic) -> RhaiResult { +fn make_variant(variant: &'static str, value: Dynamic) -> Dynamic { let mut map = crate::Map::new(); map.insert(variant.into(), value); - Ok(map.into()) + map.into() } diff --git a/src/serde/serialize.rs b/src/serde/serialize.rs index dc98ce75..4cebe930 100644 --- a/src/serde/serialize.rs +++ b/src/serde/serialize.rs @@ -58,7 +58,7 @@ impl Serialize for Dynamic { #[cfg(not(feature = "no_index"))] Union::Array(ref a, ..) => (**a).serialize(ser), #[cfg(not(feature = "no_index"))] - Union::Blob(ref a, ..) => ser.serialize_bytes(&**a), + Union::Blob(ref a, ..) => ser.serialize_bytes(a), #[cfg(not(feature = "no_object"))] Union::Map(ref m, ..) => { let mut map = ser.serialize_map(Some(m.len()))?; diff --git a/tests/serde.rs b/tests/serde.rs index 417a4857..f3c7914f 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -13,22 +13,20 @@ use rhai::Array; use rhai::Map; #[cfg(not(feature = "no_float"))] use rhai::FLOAT; -#[cfg(feature = "no_float")] #[cfg(feature = "decimal")] use rust_decimal::Decimal; #[test] fn test_serde_ser_primary_types() -> Result<(), Box> { assert!(to_dynamic(42_u64)?.is_int()); - assert!(to_dynamic(u64::MAX)?.is::()); assert!(to_dynamic(42 as INT)?.is_int()); assert!(to_dynamic(true)?.is_bool()); assert!(to_dynamic(())?.is_unit()); #[cfg(not(feature = "no_float"))] { - assert!(to_dynamic(123.456_f64)?.is::()); - assert!(to_dynamic(123.456_f32)?.is::()); + assert!(to_dynamic(123.456_f64)?.is::()); + assert!(to_dynamic(123.456_f32)?.is::()); } #[cfg(feature = "no_float")] @@ -749,6 +747,31 @@ fn test_serde_json() -> serde_json::Result<()> { Ok(()) } +#[test] +#[cfg(feature = "metadata")] +#[cfg(feature = "decimal")] +#[cfg(not(feature = "no_float"))] +fn test_serde_json_numbers() -> serde_json::Result<()> { + use std::str::FromStr; + + let d: Dynamic = serde_json::from_str("100000000000")?; + assert!(d.is::()); + assert_eq!(d.as_int().unwrap(), 100000000000); + + let d: Dynamic = serde_json::from_str("10000000000000000000")?; + assert!(d.is::()); + assert_eq!( + d.as_decimal().unwrap(), + Decimal::from_str("10000000000000000000").unwrap() + ); + + let d: Dynamic = serde_json::from_str("10000000000000000000000000")?; + assert!(d.is::()); + assert_eq!(d.as_float().unwrap(), 10000000000000000000000000.0); + + Ok(()) +} + #[test] #[cfg(not(feature = "no_object"))] fn test_serde_optional() -> Result<(), Box> {