From 94ce735adec6024b5f2126398e7a13decacdccf0 Mon Sep 17 00:00:00 2001 From: Bilal Mahmoud Date: Fri, 10 Feb 2023 16:00:48 +0100 Subject: [PATCH] `deer` implement value deserializers (#1947) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 🌟 What is the purpose of this PR? These deserializers only act upon a single value and are inspired by `serde`. They enable us to easily implement types like `Option`. ## 🔗 Related links - [Inspiration](https://docs.rs/serde/latest/serde/de/value/index.html) ## 📹 Demo --- libs/deer/src/lib.rs | 3 + libs/deer/src/macros.rs | 103 ++++++++ libs/deer/src/value.rs | 195 +++++++++++++++ libs/deer/src/value/array.rs | 48 ++++ libs/deer/src/value/bytes.rs | 102 ++++++++ libs/deer/src/value/object.rs | 48 ++++ libs/deer/src/value/string.rs | 102 ++++++++ libs/deer/tests/test_value.rs | 458 ++++++++++++++++++++++++++++++++++ 8 files changed, 1059 insertions(+) create mode 100644 libs/deer/src/macros.rs create mode 100644 libs/deer/src/value.rs create mode 100644 libs/deer/src/value/array.rs create mode 100644 libs/deer/src/value/bytes.rs create mode 100644 libs/deer/src/value/object.rs create mode 100644 libs/deer/src/value/string.rs create mode 100644 libs/deer/tests/test_value.rs diff --git a/libs/deer/src/lib.rs b/libs/deer/src/lib.rs index 3671c908294..203f2f2df16 100644 --- a/libs/deer/src/lib.rs +++ b/libs/deer/src/lib.rs @@ -50,8 +50,11 @@ use crate::{ mod context; pub mod error; mod impls; +#[macro_use] +mod macros; mod number; mod schema; +pub mod value; extern crate alloc; diff --git a/libs/deer/src/macros.rs b/libs/deer/src/macros.rs new file mode 100644 index 00000000000..048b0343054 --- /dev/null +++ b/libs/deer/src/macros.rs @@ -0,0 +1,103 @@ +// Adapted from serde + +#[macro_export(local_inner_macros)] +macro_rules! forward_to_deserialize_any { + (<$visitor:ident: Visitor<$lifetime:tt>> $($func:ident)*) => { + $(forward_to_deserialize_any_helper!{$func<$lifetime, $visitor>})* + }; + // This case must be after the previous one. + ($($func:ident)*) => { + $(forward_to_deserialize_any_helper!{$func<'de, V>})* + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! forward_to_deserialize_any_method { + ($func:ident < $l:tt, $v:ident > ()) => { + #[inline] + fn $func<$v>(self, visitor: $v) -> error_stack::Result<$v::Value, $crate::DeserializerError> + where + $v: $crate::Visitor<$l>, + { + self.deserialize_any(visitor) + } + }; +} + +#[doc(hidden)] +#[macro_export(local_inner_macros)] +macro_rules! forward_to_deserialize_any_helper { + (bool < $l:tt, $v:ident >) => { + forward_to_deserialize_any_method! {deserialize_bool<$l, $v>()} + }; + (i8 < $l:tt, $v:ident >) => { + forward_to_deserialize_any_method! {deserialize_i8<$l, $v>()} + }; + (i16 < $l:tt, $v:ident >) => { + forward_to_deserialize_any_method! {deserialize_i16<$l, $v>()} + }; + (i32 < $l:tt, $v:ident >) => { + forward_to_deserialize_any_method! {deserialize_i32<$l, $v>()} + }; + (i64 < $l:tt, $v:ident >) => { + forward_to_deserialize_any_method! {deserialize_i64<$l, $v>()} + }; + (i128 < $l:tt, $v:ident >) => { + forward_to_deserialize_any_method! {deserialize_i128<$l, $v>()} + }; + (isize < $l:tt, $v:ident >) => { + forward_to_deserialize_any_method! {deserialize_isize<$l, $v>()} + }; + (u8 < $l:tt, $v:ident >) => { + forward_to_deserialize_any_method! {deserialize_u8<$l, $v>()} + }; + (u16 < $l:tt, $v:ident >) => { + forward_to_deserialize_any_method! {deserialize_u16<$l, $v>()} + }; + (u32 < $l:tt, $v:ident >) => { + forward_to_deserialize_any_method! {deserialize_u32<$l, $v>()} + }; + (u64 < $l:tt, $v:ident >) => { + forward_to_deserialize_any_method! {deserialize_u64<$l, $v>()} + }; + (u128 < $l:tt, $v:ident >) => { + forward_to_deserialize_any_method! {deserialize_u128<$l, $v>()} + }; + (usize < $l:tt, $v:ident >) => { + forward_to_deserialize_any_method! {deserialize_usize<$l, $v>()} + }; + (f32 < $l:tt, $v:ident >) => { + forward_to_deserialize_any_method! {deserialize_f32<$l, $v>()} + }; + (f64 < $l:tt, $v:ident >) => { + forward_to_deserialize_any_method! {deserialize_f64<$l, $v>()} + }; + (char < $l:tt, $v:ident >) => { + forward_to_deserialize_any_method! {deserialize_char<$l, $v>()} + }; + (str < $l:tt, $v:ident >) => { + forward_to_deserialize_any_method! {deserialize_str<$l, $v>()} + }; + (string < $l:tt, $v:ident >) => { + forward_to_deserialize_any_method! {deserialize_string<$l, $v>()} + }; + (bytes < $l:tt, $v:ident >) => { + forward_to_deserialize_any_method! {deserialize_bytes<$l, $v>()} + }; + (bytes_buffer < $l:tt, $v:ident >) => { + forward_to_deserialize_any_method! {deserialize_bytes_buffer<$l, $v>()} + }; + (number < $l:tt, $v:ident >) => { + forward_to_deserialize_any_method! {deserialize_number<$l, $v>()} + }; + (null < $l:tt, $v:ident >) => { + forward_to_deserialize_any_method! {deserialize_null<$l, $v>()} + }; + (object < $l:tt, $v:ident >) => { + forward_to_deserialize_any_method! {deserialize_object<$l, $v>()} + }; + (array < $l:tt, $v:ident >) => { + forward_to_deserialize_any_method! {deserialize_array<$l, $v>()} + }; +} diff --git a/libs/deer/src/value.rs b/libs/deer/src/value.rs new file mode 100644 index 00000000000..ba17fca3dee --- /dev/null +++ b/libs/deer/src/value.rs @@ -0,0 +1,195 @@ +mod array; +mod bytes; +mod object; +mod string; + +pub use array::ArrayAccessDeserializer; +pub use bytes::{BorrowedBytesDeserializer, BytesBufferDeserializer, BytesDeserializer}; +use error_stack::ResultExt; +pub use object::ObjectAccessDeserializer; +pub use string::{BorrowedStrDeserializer, StrDeserializer, StringDeserializer}; + +use crate::{error::DeserializerError, Context, Deserializer, Number, Visitor}; + +macro_rules! impl_owned { + (@INTERNAL COPY, $ty:ty, $name:ident, $method:ident) => { + #[derive(Debug, Copy, Clone)] + pub struct $name<'a> { + context: &'a Context, + value: $ty + } + }; + + (@INTERNAL CLONE, $ty:ty, $name:ident, $method:ident) => { + #[derive(Debug, Clone)] + pub struct $name<'a> { + context: &'a Context, + value: $ty + } + }; + + (@INTERNAL IMPL, $ty:ty, $name:ident, $method:ident) => { + impl<'a> $name<'a> { + #[must_use] + pub const fn new(value: $ty, context: &'a Context) -> Self { + Self { value, context } + } + } + + impl<'de, 'a> Deserializer<'de> for $name<'a> { + forward_to_deserialize_any!( + null + bool + number + i8 i16 i32 i64 i128 isize + u8 u16 u32 u64 u128 usize + f32 f64 + char str string + bytes bytes_buffer + array object + ); + + fn context(&self) -> &Context { + self.context + } + + fn deserialize_any(self, visitor: V) -> error_stack::Result + where + V: Visitor<'de>, + { + visitor.$method(self.value).change_context(DeserializerError) + } + } + + impl<'de> IntoDeserializer<'de> for $ty { + type Deserializer<'a> = $name<'a> where Self: 'a; + + fn into_deserializer<'a>(self, context: &'a Context) -> Self::Deserializer<'a> + where + Self: 'a { + $name::new(self, context) + } + } + }; + + (copy: $ty:ty, $name:ident, $method:ident) => { + impl_owned!(@INTERNAL COPY, $ty, $name, $method); + impl_owned!(@INTERNAL IMPL, $ty, $name, $method); + }; + + (!copy: $ty:ty, $name:ident, $method:ident) => { + impl_owned!(@INTERNAL CLONE, $ty, $name, $method); + impl_owned!(@INTERNAL IMPL, $ty, $name, $method); + }; + + ($ty:ty, $name:ident, $method:ident) => { + impl_owned!(copy: $ty, $name, $method); + }; +} + +use impl_owned; + +pub trait IntoDeserializer<'de> { + type Deserializer<'a>: Deserializer<'de> + where + Self: 'a; + + fn into_deserializer<'a>(self, context: &'a Context) -> Self::Deserializer<'a> + where + Self: 'a; +} + +#[derive(Debug, Copy, Clone)] +pub struct NoneDeserializer<'a> { + context: &'a Context, +} + +impl<'a> NoneDeserializer<'a> { + #[must_use] + pub const fn new(context: &'a Context) -> Self { + Self { context } + } +} + +impl<'de> Deserializer<'de> for NoneDeserializer<'_> { + forward_to_deserialize_any!( + null + bool + number + i8 i16 i32 i64 i128 isize + u8 u16 u32 u64 u128 usize + f32 f64 + char str string + bytes bytes_buffer + array object + ); + + fn context(&self) -> &Context { + self.context + } + + fn deserialize_any(self, visitor: V) -> error_stack::Result + where + V: Visitor<'de>, + { + visitor.visit_none().change_context(DeserializerError) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct NullDeserializer<'a> { + context: &'a Context, +} + +impl<'a> NullDeserializer<'a> { + #[must_use] + pub const fn new(context: &'a Context) -> Self { + Self { context } + } +} + +impl<'de> Deserializer<'de> for NullDeserializer<'_> { + forward_to_deserialize_any!( + null + bool + number + i8 i16 i32 i64 i128 isize + u8 u16 u32 u64 u128 usize + f32 f64 + char str string + bytes bytes_buffer + array object + ); + + fn context(&self) -> &Context { + self.context + } + + fn deserialize_any(self, visitor: V) -> error_stack::Result + where + V: Visitor<'de>, + { + visitor.visit_null().change_context(DeserializerError) + } +} + +impl_owned!(bool, BoolDeserializer, visit_bool); +impl_owned!(char, CharDeserializer, visit_char); +impl_owned!(u8, U8Deserializer, visit_u8); +impl_owned!(u16, U16Deserializer, visit_u16); +impl_owned!(u32, U32Deserializer, visit_u32); +impl_owned!(u64, U64Deserializer, visit_u64); +impl_owned!(u128, U128Deserializer, visit_u128); +impl_owned!(usize, UsizeDeserializer, visit_usize); +impl_owned!(i8, I8Deserializer, visit_i8); +impl_owned!(i16, I16Deserializer, visit_i16); +impl_owned!(i32, I32Deserializer, visit_i32); +impl_owned!(i64, I64Deserializer, visit_i64); +impl_owned!(i128, I128Deserializer, visit_i128); +impl_owned!(isize, IsizeDeserializer, visit_isize); +impl_owned!(f32, F32Deserializer, visit_f32); +impl_owned!(f64, F64Deserializer, visit_f64); + +impl_owned!(!copy: Number, NumberDeserializer, visit_number); + +// TODO: test diff --git a/libs/deer/src/value/array.rs b/libs/deer/src/value/array.rs new file mode 100644 index 00000000000..e3802129b2e --- /dev/null +++ b/libs/deer/src/value/array.rs @@ -0,0 +1,48 @@ +use error_stack::{Result, ResultExt}; + +use crate::{error::DeserializerError, ArrayAccess, Context, Deserializer, Visitor}; + +// TODO: SliceDeserializer/IteratorDeserializer + +#[derive(Debug)] +pub struct ArrayAccessDeserializer<'a, T> { + context: &'a Context, + value: T, +} + +impl<'a, T> ArrayAccessDeserializer<'a, T> { + #[must_use] + pub const fn new(context: &'a Context, value: T) -> Self { + Self { context, value } + } +} + +impl<'de, T> Deserializer<'de> for ArrayAccessDeserializer<'_, T> +where + T: ArrayAccess<'de>, +{ + forward_to_deserialize_any!( + null + bool + number + i8 i16 i32 i64 i128 isize + u8 u16 u32 u64 u128 usize + f32 f64 + char str string + bytes bytes_buffer + array object + ); + + fn context(&self) -> &Context { + self.context + } + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor + .visit_array(self.value) + .change_context(DeserializerError) + } +} diff --git a/libs/deer/src/value/bytes.rs b/libs/deer/src/value/bytes.rs new file mode 100644 index 00000000000..5c2f2d729af --- /dev/null +++ b/libs/deer/src/value/bytes.rs @@ -0,0 +1,102 @@ +use alloc::vec::Vec; + +use error_stack::ResultExt; + +use crate::{ + error::DeserializerError, + value::{impl_owned, IntoDeserializer}, + Context, Deserializer, Visitor, +}; + +#[derive(Debug, Copy, Clone)] +pub struct BytesDeserializer<'a, 'b> { + context: &'a Context, + value: &'b [u8], +} + +impl<'a, 'b> BytesDeserializer<'a, 'b> { + #[must_use] + pub const fn new(value: &'b [u8], context: &'a Context) -> Self { + Self { context, value } + } +} + +impl<'de> Deserializer<'de> for BytesDeserializer<'_, '_> { + forward_to_deserialize_any!( + null + bool + number + i8 i16 i32 i64 i128 isize + u8 u16 u32 u64 u128 usize + f32 f64 + char str string + bytes bytes_buffer + array object + ); + + fn context(&self) -> &Context { + self.context + } + + fn deserialize_any(self, visitor: V) -> error_stack::Result + where + V: Visitor<'de>, + { + visitor + .visit_bytes(self.value) + .change_context(DeserializerError) + } +} + +impl<'de, 'b> IntoDeserializer<'de> for &'b [u8] { + type Deserializer<'a> = BytesDeserializer<'a, 'b> where Self: 'a; + + fn into_deserializer<'a>(self, context: &'a Context) -> Self::Deserializer<'a> + where + Self: 'a, + { + BytesDeserializer::new(self, context) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct BorrowedBytesDeserializer<'a, 'de> { + context: &'a Context, + value: &'de [u8], +} + +impl<'a, 'de> BorrowedBytesDeserializer<'a, 'de> { + #[must_use] + pub const fn new(value: &'de [u8], context: &'a Context) -> Self { + Self { context, value } + } +} + +impl<'de> Deserializer<'de> for BorrowedBytesDeserializer<'_, 'de> { + forward_to_deserialize_any!( + null + bool + number + i8 i16 i32 i64 i128 isize + u8 u16 u32 u64 u128 usize + f32 f64 + char str string + bytes bytes_buffer + array object + ); + + fn context(&self) -> &Context { + self.context + } + + fn deserialize_any(self, visitor: V) -> error_stack::Result + where + V: Visitor<'de>, + { + visitor + .visit_borrowed_bytes(self.value) + .change_context(DeserializerError) + } +} + +impl_owned!(!copy: Vec, BytesBufferDeserializer, visit_bytes_buffer); diff --git a/libs/deer/src/value/object.rs b/libs/deer/src/value/object.rs new file mode 100644 index 00000000000..97347f1ce6d --- /dev/null +++ b/libs/deer/src/value/object.rs @@ -0,0 +1,48 @@ +use error_stack::{Result, ResultExt}; + +use crate::{error::DeserializerError, Context, Deserializer, ObjectAccess, Visitor}; + +// TODO: MapDeserializer/IteratorDeserializer + +#[derive(Debug)] +pub struct ObjectAccessDeserializer<'a, T> { + context: &'a Context, + value: T, +} + +impl<'a, T> ObjectAccessDeserializer<'a, T> { + #[must_use] + pub const fn new(context: &'a Context, value: T) -> Self { + Self { context, value } + } +} + +impl<'de, T> Deserializer<'de> for ObjectAccessDeserializer<'_, T> +where + T: ObjectAccess<'de>, +{ + forward_to_deserialize_any!( + null + bool + number + i8 i16 i32 i64 i128 isize + u8 u16 u32 u64 u128 usize + f32 f64 + char str string + bytes bytes_buffer + array object + ); + + fn context(&self) -> &Context { + self.context + } + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor + .visit_object(self.value) + .change_context(DeserializerError) + } +} diff --git a/libs/deer/src/value/string.rs b/libs/deer/src/value/string.rs new file mode 100644 index 00000000000..59941907742 --- /dev/null +++ b/libs/deer/src/value/string.rs @@ -0,0 +1,102 @@ +use alloc::string::String; + +use error_stack::ResultExt; + +use crate::{ + error::DeserializerError, + value::{impl_owned, IntoDeserializer}, + Context, Deserializer, Visitor, +}; + +#[derive(Debug, Copy, Clone)] +pub struct StrDeserializer<'a, 'b> { + context: &'a Context, + value: &'b str, +} + +impl<'a, 'b> StrDeserializer<'a, 'b> { + #[must_use] + pub const fn new(value: &'b str, context: &'a Context) -> Self { + Self { context, value } + } +} + +impl<'de> Deserializer<'de> for StrDeserializer<'_, '_> { + forward_to_deserialize_any!( + null + bool + number + i8 i16 i32 i64 i128 isize + u8 u16 u32 u64 u128 usize + f32 f64 + char str string + bytes bytes_buffer + array object + ); + + fn context(&self) -> &Context { + self.context + } + + fn deserialize_any(self, visitor: V) -> error_stack::Result + where + V: Visitor<'de>, + { + visitor + .visit_str(self.value) + .change_context(DeserializerError) + } +} + +impl<'de, 'b> IntoDeserializer<'de> for &'b str { + type Deserializer<'a> = StrDeserializer<'a, 'b> where Self: 'a; + + fn into_deserializer<'a>(self, context: &'a Context) -> Self::Deserializer<'a> + where + Self: 'a, + { + StrDeserializer::new(self, context) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct BorrowedStrDeserializer<'a, 'de> { + context: &'a Context, + value: &'de str, +} + +impl<'a, 'de> BorrowedStrDeserializer<'a, 'de> { + #[must_use] + pub const fn new(value: &'de str, context: &'a Context) -> Self { + Self { context, value } + } +} + +impl<'de> Deserializer<'de> for BorrowedStrDeserializer<'_, 'de> { + forward_to_deserialize_any!( + null + bool + number + i8 i16 i32 i64 i128 isize + u8 u16 u32 u64 u128 usize + f32 f64 + char str string + bytes bytes_buffer + array object + ); + + fn context(&self) -> &Context { + self.context + } + + fn deserialize_any(self, visitor: V) -> error_stack::Result + where + V: Visitor<'de>, + { + visitor + .visit_borrowed_str(self.value) + .change_context(DeserializerError) + } +} + +impl_owned!(!copy: String, StringDeserializer, visit_string); diff --git a/libs/deer/tests/test_value.rs b/libs/deer/tests/test_value.rs new file mode 100644 index 00000000000..a897d5a892f --- /dev/null +++ b/libs/deer/tests/test_value.rs @@ -0,0 +1,458 @@ +use deer::{ + error::{DeserializeError, ExpectedType, ReceivedValue, ValueError, Variant, VisitorError}, + value::{ + BoolDeserializer, BorrowedBytesDeserializer, BorrowedStrDeserializer, + BytesBufferDeserializer, BytesDeserializer, CharDeserializer, F32Deserializer, + F64Deserializer, I128Deserializer, I16Deserializer, I32Deserializer, I64Deserializer, + I8Deserializer, IntoDeserializer, IsizeDeserializer, NullDeserializer, NumberDeserializer, + StrDeserializer, U128Deserializer, U16Deserializer, U32Deserializer, U64Deserializer, + U8Deserializer, UsizeDeserializer, + }, + Context, Deserialize, Deserializer, Document, Number, Reflection, Schema, Visitor, +}; +use error_stack::{Report, Result, ResultExt}; +use proptest::prelude::*; + +macro_rules! generate_proptest { + ($ty:ty,not($err:ty)) => { + paste::paste! { + #[test] + #[cfg(not(miri))] + fn [< $ty _ok >]() { + let context = Context::new(); + + proptest!(move |(expected in any::<$ty>())| { + let de = [< $ty:camel Deserializer >]::new(expected, &context); + let received = <$ty>::deserialize(de).expect("able to deserialize"); + + assert_eq!(expected, received); + }); + } + + #[test] + #[cfg(not(miri))] + fn [< $ty _into_deserializer_ok >]() { + let context = Context::new(); + + proptest!(move |(expected in any::<$ty>())| { + let de = expected.into_deserializer(&context); + let received = <$ty>::deserialize(de).expect("able to deserialize"); + + assert_eq!(expected, received); + }); + } + + #[test] + #[cfg(not(miri))] + fn [< $ty _err >]() { + let context = Context::new(); + + proptest!(move |(expected in any::<$ty>())| { + let de = [< $ty:camel Deserializer >]::new(expected, &context); + let result = <$err>::deserialize(de); + + assert!(result.is_err()); + }); + } + } + }; +} + +generate_proptest!(u8, not(u16)); +generate_proptest!(u16, not(u32)); +generate_proptest!(u32, not(u64)); +generate_proptest!(u64, not(u128)); +generate_proptest!(u128, not(u8)); +generate_proptest!(usize, not(u8)); +generate_proptest!(i8, not(i16)); +generate_proptest!(i16, not(i32)); +generate_proptest!(i32, not(i64)); +generate_proptest!(i64, not(i128)); +generate_proptest!(i128, not(i8)); +generate_proptest!(isize, not(i8)); +generate_proptest!(f32, not(i8)); +generate_proptest!(f64, not(i8)); +generate_proptest!(bool, not(i8)); +generate_proptest!(char, not(i8)); + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum Choice { + Yes, + No, +} + +impl Choice { + const fn into_str(self) -> &'static str { + match self { + Choice::Yes => "yes", + Choice::No => "no", + } + } +} + +impl Reflection for Choice { + fn schema(doc: &mut Document) -> Schema { + str::schema(doc).with("enum", ["yes", "no"]) + } +} + +struct ChoiceVisitor; + +impl<'de> Visitor<'de> for ChoiceVisitor { + type Value = Choice; + + fn expecting(&self) -> Document { + Self::Value::document() + } + + fn visit_str(self, v: &str) -> Result { + match v { + "yes" => Ok(Choice::Yes), + "no" => Ok(Choice::No), + other => Err(Report::new(ValueError.into_error()) + .attach(ReceivedValue::new(other.to_owned())) + .attach(ExpectedType::new(self.expecting())) + .change_context(VisitorError)), + } + } +} + +impl<'de> Deserialize<'de> for Choice { + type Reflection = Self; + + fn deserialize>(de: D) -> Result { + de.deserialize_str(ChoiceVisitor) + .change_context(DeserializeError) + } +} + +struct Null; + +impl Reflection for Null { + fn schema(_: &mut Document) -> Schema { + Schema::new("null") + } +} + +struct NullVisitor; + +impl<'de> Visitor<'de> for NullVisitor { + type Value = Null; + + fn expecting(&self) -> Document { + Self::Value::document() + } + + fn visit_null(self) -> Result { + Ok(Null) + } +} + +impl<'de> Deserialize<'de> for Null { + type Reflection = Self; + + fn deserialize>(de: D) -> Result { + de.deserialize_null(NullVisitor) + .change_context(DeserializeError) + } +} + +prop_compose! { + fn choice_strategy()(base in any::()) -> Choice { + if base {Choice::Yes} else {Choice::No} + } +} + +#[cfg(not(miri))] +proptest! { + #[test] + fn str_ok(expected in choice_strategy()) { + let context = Context::new(); + + + let de = StrDeserializer::new(expected.into_str(), &context); + let received = Choice::deserialize(de).expect("able to deserialize"); + + assert_eq!(expected, received); + } + + #[test] + fn str_into_deserializer_ok(expected in choice_strategy()) { + let context = Context::new(); + + + let de = expected.into_str().into_deserializer(&context); + let received = Choice::deserialize(de).expect("able to deserialize"); + + assert_eq!(expected, received); + } + + #[test] + fn str_err(expected in any::()) { + let context = Context::new(); + + let de = StrDeserializer::new(&expected, &context); + let result = u8::deserialize(de); + + assert!(result.is_err()); + } + + #[test] + fn borrowed_str_ok(expected in any::()) { + let context = Context::new(); + let value = expected.as_str(); + + let de = BorrowedStrDeserializer::new(value, &context); + let received = <&str>::deserialize(de).expect("able to deserialize"); + + assert_eq!(expected, received); + } + + #[test] + fn borrowed_str_err(expected in any::()) { + let context = Context::new(); + let value = expected.as_str(); + + let de = BorrowedStrDeserializer::new(value, &context); + let result = u8::deserialize(de); + + assert!(result.is_err()); + } + + // TODO: deserialize no yet implemented for alloc + // #[test] + // fn string_ok(expected in any::()) { + // let context = Context::new(); + // + // let de = StringDeserializer::new(expected, &context); + // let received = String::deserialize(de).expect("able to deserialize"); + // + // assert_eq!(expected, received); + // } + // + // #[test] + // fn string_err(expected in any::()) { + // let context = Context::new(); + // + // let de = StringDeserializer::new(expected, &context); + // let result = u8::deserialize(de); + // + // assert!(result.is_err()); + // } + + #[test] + fn number_ok(expected in any::()) { + let context = Context::new(); + let value = Number::from(expected); + + let de = NumberDeserializer::new(value.clone(), &context); + let received = Number::deserialize(de).expect("able to deserialize"); + + assert_eq!(value, received); + } + + #[test] + fn number_err(expected in any::()) { + let context = Context::new(); + let value = Number::from(expected); + + let de = NumberDeserializer::new(value, &context); + let result = <&str>::deserialize(de); + + assert!(result.is_err()); + } +} + +#[test] +fn null_ok() { + let context = Context::new(); + + let de = NullDeserializer::new(&context); + let _ = Null::deserialize(de).expect("able to deserializer"); +} + +#[test] +fn null_err() { + let context = Context::new(); + + let de = NullDeserializer::new(&context); + let result = u8::deserialize(de); + + assert!(result.is_err()); +} + +struct Bytes<'a>(&'a [u8]); + +impl Reflection for Bytes<'static> { + fn schema(_: &mut Document) -> Schema { + Schema::new("binary") + } +} + +struct BytesVisitor; + +impl<'de> Visitor<'de> for BytesVisitor { + type Value = Bytes<'de>; + + fn expecting(&self) -> Document { + Bytes::document() + } + + fn visit_borrowed_bytes(self, v: &'de [u8]) -> Result { + Ok(Bytes(v)) + } +} + +impl<'de: 'a, 'a> Deserialize<'de> for Bytes<'a> { + type Reflection = Bytes<'static>; + + fn deserialize>(de: D) -> Result { + de.deserialize_bytes(BytesVisitor) + .change_context(DeserializeError) + } +} + +struct BytesLength(usize); + +impl Reflection for BytesLength { + fn schema(doc: &mut Document) -> Schema { + usize::schema(doc) + } +} + +struct BytesLengthVisitor; + +impl<'de> Visitor<'de> for BytesLengthVisitor { + type Value = BytesLength; + + fn expecting(&self) -> Document { + Self::Value::document() + } + + fn visit_bytes(self, v: &[u8]) -> Result { + Ok(BytesLength(v.len())) + } +} + +impl<'de> Deserialize<'de> for BytesLength { + type Reflection = Self; + + fn deserialize>(de: D) -> Result { + de.deserialize_bytes(BytesLengthVisitor) + .change_context(DeserializeError) + } +} + +struct ByteBuffer(Vec); + +impl Reflection for ByteBuffer { + fn schema(_: &mut Document) -> Schema { + Schema::new("binary") + } +} + +struct ByteBufferVisitor; + +impl<'de> Visitor<'de> for ByteBufferVisitor { + type Value = ByteBuffer; + + fn expecting(&self) -> Document { + Self::Value::document() + } + + fn visit_bytes_buffer(self, v: Vec) -> Result { + Ok(ByteBuffer(v)) + } +} + +impl<'de> Deserialize<'de> for ByteBuffer { + type Reflection = Self; + + fn deserialize>(de: D) -> Result { + de.deserialize_bytes_buffer(ByteBufferVisitor) + .change_context(DeserializeError) + } +} + +#[cfg(not(miri))] +proptest! { + #[test] + fn borrowed_bytes_ok(expected in any::>()) { + let context = Context::new(); + let value = expected.as_slice(); + + let de = BorrowedBytesDeserializer::new(value, &context); + let received = Bytes::deserialize(de).expect("should be able to deserialize"); + + assert_eq!(value, received.0); + } + + #[test] + fn borrowed_bytes_err(expected in any::>()) { + let context = Context::new(); + let value = expected.as_slice(); + + let de = BorrowedBytesDeserializer::new(value, &context); + let result = u8::deserialize(de); + + assert!(result.is_err()); + } + + #[test] + fn bytes_ok(expected in any::>()) { + let context = Context::new(); + let value = expected.as_slice(); + + let de = BytesDeserializer::new(value, &context); + let received = BytesLength::deserialize(de).expect("should be able to deserialize"); + + assert_eq!(value.len(), received.0); + } + + #[test] + fn bytes_into_deserializer_ok(expected in any::>()) { + let context = Context::new(); + let value = expected.as_slice(); + + let de = value.into_deserializer(&context); + let received = BytesLength::deserialize(de).expect("should be able to deserialize"); + + assert_eq!(value.len(), received.0); + } + + #[test] + fn bytes_err(expected in any::>()) { + let context = Context::new(); + let value = expected.as_slice(); + + let de = BytesDeserializer::new(value, &context); + let result = u8::deserialize(de); + + assert!(result.is_err()); + } + + #[test] + fn byte_buffer_ok(expected in any::>()) { + let context = Context::new(); + + let de = BytesBufferDeserializer::new(expected.clone(), &context); + let received = ByteBuffer::deserialize(de).expect("should be able to deserialize"); + + assert_eq!(expected, received.0); + } + + #[test] + fn byte_buffer_err(expected in any::>()) { + let context = Context::new(); + + let de = BytesBufferDeserializer::new(expected, &context); + let result = u8::deserialize(de); + + assert!(result.is_err()); + } +} + +// These are so trivial that we don't need to test them right now: ArrayAccess, ObjectAccess +// (would be nice tho) +// TODO: none requires a HashMap<> impl first +// TODO: string requires a String impl first