diff --git a/libs/deer/desert/src/assert.rs b/libs/deer/desert/src/assert.rs index 01cb3d2b857..4eaf8a7dcb4 100644 --- a/libs/deer/desert/src/assert.rs +++ b/libs/deer/desert/src/assert.rs @@ -10,16 +10,31 @@ use crate::{deserializer::Deserializer, error::ErrorVec, token::Token}; /// # Panics /// /// if there are any remaining tokens in the stream after deserialization -pub fn assert_tokens_with_context<'de, T>(expected: &T, tokens: &'de [Token], context: &Context) -where - T: Deserialize<'de> + PartialEq + Debug, +pub fn assert_tokens_deserialize<'de, T>( + tokens: &'de [Token], + context: &Context, + assertion: impl FnOnce(T), +) where + T: Deserialize<'de>, { let mut de = Deserializer::new(tokens, context); let received = T::deserialize(&mut de).expect("should deserialize"); assert_eq!(de.remaining(), 0, "{} remaining tokens", de.remaining()); - assert_eq!(received, *expected); + assertion(received); +} + +/// # Panics +/// +/// if there are any remaining tokens in the stream after deserialization +pub fn assert_tokens_with_context<'de, T>(expected: &T, tokens: &'de [Token], context: &Context) +where + T: Deserialize<'de> + PartialEq + Debug, +{ + assert_tokens_deserialize::(tokens, context, |received| { + assert_eq!(received, *expected); + }); } pub fn assert_tokens<'de, T>(value: &T, tokens: &'de [Token]) @@ -29,6 +44,13 @@ where assert_tokens_with_context(value, tokens, &Context::new()); } +pub fn assert_tokens_with_assertion<'de, T>(assertion: impl FnOnce(T), tokens: &'de [Token]) +where + T: Deserialize<'de>, +{ + assert_tokens_deserialize::(tokens, &Context::new(), assertion); +} + /// # Panics /// /// if error could not be serialized diff --git a/libs/deer/desert/src/lib.rs b/libs/deer/desert/src/lib.rs index f4496c2d18f..3e440cc2c20 100644 --- a/libs/deer/desert/src/lib.rs +++ b/libs/deer/desert/src/lib.rs @@ -11,7 +11,7 @@ pub(crate) mod tape; mod token; pub use assert::{ - assert_tokens, assert_tokens_error, assert_tokens_with_context, - assert_tokens_with_context_error, + assert_tokens, assert_tokens_deserialize, assert_tokens_error, assert_tokens_with_assertion, + assert_tokens_with_context, assert_tokens_with_context_error, }; pub use token::Token; diff --git a/libs/deer/src/impls/core.rs b/libs/deer/src/impls/core.rs index 34d839e1ec1..9c0952c85f6 100644 --- a/libs/deer/src/impls/core.rs +++ b/libs/deer/src/impls/core.rs @@ -1,6 +1,7 @@ mod array; mod atomic; mod bool; +mod cell; mod cmp; mod floating; mod integral; diff --git a/libs/deer/src/impls/core/cell.rs b/libs/deer/src/impls/core/cell.rs new file mode 100644 index 00000000000..31e2e8c55a7 --- /dev/null +++ b/libs/deer/src/impls/core/cell.rs @@ -0,0 +1,47 @@ +use core::cell::{Cell, RefCell, UnsafeCell}; +#[cfg(nightly)] +use core::cell::{OnceCell, SyncUnsafeCell}; + +use crate::{error::DeserializeError, Deserialize, Deserializer, Document, Reflection, Schema}; + +macro_rules! impl_cell { + ($(#[$attr:meta])* $cell:ident) => { + $(#[$attr])* + impl Reflection for $cell + where + T: Reflection, + { + fn schema(doc: &mut Document) -> Schema { + T::schema(doc) + } + } + + $(#[$attr])* + impl<'de, T> Deserialize<'de> for $cell + where + T: Deserialize<'de>, + { + type Reflection = T::Reflection; + + fn deserialize>( + de: D, + ) -> error_stack::Result { + T::deserialize(de).map(Self::from) + } + } + }; + + ($($(#[$attr:meta])* $cell:ident),+ $(,)?) => { + $(impl_cell!($(#[$attr])* $cell);)* + }; +} + +impl_cell![ + #[cfg(nightly)] + OnceCell, + #[cfg(nightly)] + SyncUnsafeCell, + Cell, + RefCell, + UnsafeCell +]; diff --git a/libs/deer/src/lib.rs b/libs/deer/src/lib.rs index fea0d1b1047..1ed5886f7a4 100644 --- a/libs/deer/src/lib.rs +++ b/libs/deer/src/lib.rs @@ -4,7 +4,8 @@ provide_any, error_in_core, error_generic_member_access, - integer_atomics + integer_atomics, + sync_unsafe_cell ) )] #![cfg_attr(not(feature = "std"), no_std)] diff --git a/libs/deer/tests/test_impls_core_cell.rs b/libs/deer/tests/test_impls_core_cell.rs new file mode 100644 index 00000000000..07600c2965f --- /dev/null +++ b/libs/deer/tests/test_impls_core_cell.rs @@ -0,0 +1,107 @@ +#![cfg_attr(nightly, feature(sync_unsafe_cell))] +use core::cell::{Cell, RefCell, UnsafeCell}; +#[cfg(nightly)] +use core::cell::{OnceCell, SyncUnsafeCell}; + +use deer::{Deserialize, Number}; +use deer_desert::{assert_tokens, assert_tokens_with_assertion, Token}; +use proptest::prelude::*; +use serde::Serialize; +use similar_asserts::assert_serde_eq; + +#[cfg(not(miri))] +proptest! { + #[test] + fn cell_ok(value in any::()) { + let expected = Cell::new(value); + + assert_tokens(&expected, &[Token::Number(Number::from(value))]); + } + + #[test] + fn ref_cell_ok(value in any::()) { + let expected = RefCell::new(value); + + assert_tokens(&expected, &[Token::Number(Number::from(value))]); + } + + #[test] + fn unsafe_cell_ok(value in any::()) { + // need to use the underlying function, because `UnsafeCell` does not expose PartialEq + assert_tokens_with_assertion(|mut received: UnsafeCell| { + assert_eq!(*received.get_mut(), value); + }, &[Token::Number( + Number::from(value), + )]); + } + + + #[cfg(nightly)] + #[test] + fn once_cell_ok(value in any::()) { + let expected: OnceCell<_> = value.into(); + + assert_tokens(&expected, &[Token::Number(Number::from(value))]); + } + + #[cfg(nightly)] + #[test] + fn sync_unsafe_cell_ok(value in any::()) { + // need to use the underlying function, because `SyncUnsafeCell` does not expose PartialEq + assert_tokens_with_assertion(|mut received: SyncUnsafeCell| { + assert_eq!(*received.get_mut(), value); + }, &[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 cell_reflection_same() { + let lhs = Cell::::reflection(); + let rhs = u8::reflection(); + + assert_json(lhs, rhs); +} + +#[test] +fn ref_cell_reflection_same() { + let lhs = RefCell::::reflection(); + let rhs = u8::reflection(); + + assert_json(lhs, rhs); +} + +#[test] +fn unsafe_cell_reflection_same() { + let lhs = UnsafeCell::::reflection(); + let rhs = u8::reflection(); + + assert_json(lhs, rhs); +} + +#[cfg(nightly)] +#[test] +fn once_cell_reflection_same() { + let lhs = OnceCell::::reflection(); + let rhs = u8::reflection(); + + assert_json(lhs, rhs); +} + +#[cfg(nightly)] +#[test] +fn sync_unsafe_cell_reflection_same() { + let lhs = SyncUnsafeCell::::reflection(); + let rhs = u8::reflection(); + + assert_json(lhs, rhs); +}