Skip to content

Commit

Permalink
deer: implement Deserialize for core::cell (#2376)
Browse files Browse the repository at this point in the history
* feat: init cell

* feat: pull in code from #1875

* feat: test cells

* test: unsafe variants

* test: reflection passthrough

* fix: lint

* Update test_impls_core_cell.rs
  • Loading branch information
indietyp authored Apr 13, 2023
1 parent eac71ae commit 5df489a
Show file tree
Hide file tree
Showing 6 changed files with 185 additions and 7 deletions.
30 changes: 26 additions & 4 deletions libs/deer/desert/src/assert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<T>(tokens, context, |received| {
assert_eq!(received, *expected);
});
}

pub fn assert_tokens<'de, T>(value: &T, tokens: &'de [Token])
Expand All @@ -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::<T>(tokens, &Context::new(), assertion);
}

/// # Panics
///
/// if error could not be serialized
Expand Down
4 changes: 2 additions & 2 deletions libs/deer/desert/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
1 change: 1 addition & 0 deletions libs/deer/src/impls/core.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod array;
mod atomic;
mod bool;
mod cell;
mod cmp;
mod floating;
mod integral;
Expand Down
47 changes: 47 additions & 0 deletions libs/deer/src/impls/core/cell.rs
Original file line number Diff line number Diff line change
@@ -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<T> Reflection for $cell<T>
where
T: Reflection,
{
fn schema(doc: &mut Document) -> Schema {
T::schema(doc)
}
}

$(#[$attr])*
impl<'de, T> Deserialize<'de> for $cell<T>
where
T: Deserialize<'de>,
{
type Reflection = T::Reflection;

fn deserialize<D: Deserializer<'de>>(
de: D,
) -> error_stack::Result<Self, DeserializeError> {
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
];
3 changes: 2 additions & 1 deletion libs/deer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
107 changes: 107 additions & 0 deletions libs/deer/tests/test_impls_core_cell.rs
Original file line number Diff line number Diff line change
@@ -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::<u8>()) {
let expected = Cell::new(value);

assert_tokens(&expected, &[Token::Number(Number::from(value))]);
}

#[test]
fn ref_cell_ok(value in any::<u8>()) {
let expected = RefCell::new(value);

assert_tokens(&expected, &[Token::Number(Number::from(value))]);
}

#[test]
fn unsafe_cell_ok(value in any::<u8>()) {
// need to use the underlying function, because `UnsafeCell` does not expose PartialEq
assert_tokens_with_assertion(|mut received: UnsafeCell<u8>| {
assert_eq!(*received.get_mut(), value);
}, &[Token::Number(
Number::from(value),
)]);
}


#[cfg(nightly)]
#[test]
fn once_cell_ok(value in any::<u8>()) {
let expected: OnceCell<_> = value.into();

assert_tokens(&expected, &[Token::Number(Number::from(value))]);
}

#[cfg(nightly)]
#[test]
fn sync_unsafe_cell_ok(value in any::<u8>()) {
// need to use the underlying function, because `SyncUnsafeCell` does not expose PartialEq
assert_tokens_with_assertion(|mut received: SyncUnsafeCell<u8>| {
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::<u8>::reflection();
let rhs = u8::reflection();

assert_json(lhs, rhs);
}

#[test]
fn ref_cell_reflection_same() {
let lhs = RefCell::<u8>::reflection();
let rhs = u8::reflection();

assert_json(lhs, rhs);
}

#[test]
fn unsafe_cell_reflection_same() {
let lhs = UnsafeCell::<u8>::reflection();
let rhs = u8::reflection();

assert_json(lhs, rhs);
}

#[cfg(nightly)]
#[test]
fn once_cell_reflection_same() {
let lhs = OnceCell::<u8>::reflection();
let rhs = u8::reflection();

assert_json(lhs, rhs);
}

#[cfg(nightly)]
#[test]
fn sync_unsafe_cell_reflection_same() {
let lhs = SyncUnsafeCell::<u8>::reflection();
let rhs = u8::reflection();

assert_json(lhs, rhs);
}

0 comments on commit 5df489a

Please sign in to comment.