diff --git a/libs/deer/src/impls/core.rs b/libs/deer/src/impls/core.rs index 75749330d69..3ec812572b9 100644 --- a/libs/deer/src/impls/core.rs +++ b/libs/deer/src/impls/core.rs @@ -6,7 +6,7 @@ mod floating; mod integral; mod marker; mod mem; -mod non_zero; +mod num; mod option; mod string; mod sync; diff --git a/libs/deer/src/impls/core/non_zero.rs b/libs/deer/src/impls/core/num.rs similarity index 77% rename from libs/deer/src/impls/core/non_zero.rs rename to libs/deer/src/impls/core/num.rs index 6bd5b2d282b..39064bdc4ef 100644 --- a/libs/deer/src/impls/core/non_zero.rs +++ b/libs/deer/src/impls/core/num.rs @@ -1,6 +1,8 @@ +#[cfg(nightly)] +use core::num::Saturating; use core::num::{ NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, - NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, + NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, Wrapping, }; use error_stack::{Report, ResultExt}; @@ -69,3 +71,20 @@ impl_nonzero![ NonZeroI128 <- i128, NonZeroIsize <- isize, ]; + +impl<'de, T: Deserialize<'de>> Deserialize<'de> for Wrapping { + type Reflection = T::Reflection; + + fn deserialize>(de: D) -> error_stack::Result { + T::deserialize(de).map(Self) + } +} + +#[cfg(nightly)] +impl<'de, T: Deserialize<'de>> Deserialize<'de> for Saturating { + type Reflection = T::Reflection; + + fn deserialize>(de: D) -> error_stack::Result { + T::deserialize(de).map(Self) + } +} diff --git a/libs/deer/src/lib.rs b/libs/deer/src/lib.rs index 353ff4bd261..7f6d1745d9a 100644 --- a/libs/deer/src/lib.rs +++ b/libs/deer/src/lib.rs @@ -5,6 +5,7 @@ error_in_core, error_generic_member_access, integer_atomics, + saturating_int_impl, sync_unsafe_cell, exclusive_wrapper ) diff --git a/libs/deer/tests/test_impls_core_non_zero.rs b/libs/deer/tests/test_impls_core_num.rs similarity index 66% rename from libs/deer/tests/test_impls_core_non_zero.rs rename to libs/deer/tests/test_impls_core_num.rs index 2dbe70d11b6..8956155d01d 100644 --- a/libs/deer/tests/test_impls_core_non_zero.rs +++ b/libs/deer/tests/test_impls_core_num.rs @@ -1,4 +1,11 @@ -// These values use the same logic as integral, but zero is an error! +#![cfg_attr(nightly, feature(saturating_int_impl))] + +#[cfg(nightly)] +use core::num::Saturating; +use core::num::Wrapping; + +use deer_desert::assert_tokens; +use proptest::prelude::*; mod common; @@ -9,8 +16,11 @@ use core::num::{ use deer::{Deserialize, Number}; use deer_desert::{assert_tokens_error, error, Token}; +use serde::Serialize; use serde_json::json; +use similar_asserts::assert_serde_eq; +// These values use the same logic as integral, but zero is an error! macro_rules! test_zero { ($ty:ident) => { paste::paste! { @@ -106,3 +116,46 @@ fn usize_err_zero() { &[Token::USize(0)], ); } + +#[cfg(not(miri))] +proptest! { + #[test] + fn wrapping_ok(value in any::()) { + let expected = Wrapping(value); + + assert_tokens(&expected, &[Token::Number(Number::from(value))]); + } + + #[cfg(nightly)] + #[test] + fn saturating_ok(value in any::()) { + let expected = Saturating(value); + + assert_tokens(&expected, &[Token::Number(Number::from(value))]); + } +} + +fn assert_json(lhs: impl Serialize, rhs: impl Serialize) { + let lhs = serde_json::to_value(lhs).expect("should be able to serialize lhs"); + let rhs = serde_json::to_value(rhs).expect("should be able to serialize rhs"); + + assert_serde_eq!(lhs, rhs); +} + +// test that the `Reflection` of all types are the same as their underlying type +#[test] +fn wrapping_reflection_same() { + let lhs = Wrapping::::reflection(); + let rhs = u8::reflection(); + + assert_json(lhs, rhs); +} + +#[cfg(nightly)] +#[test] +fn saturating_reflection_same() { + let lhs = Saturating::::reflection(); + let rhs = u8::reflection(); + + assert_json(lhs, rhs); +}