Skip to content

Commit

Permalink
deer implement value deserializers (#1947)
Browse files Browse the repository at this point in the history
## 🌟 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<T>`.

## 🔗 Related links

- [Inspiration](https://docs.rs/serde/latest/serde/de/value/index.html)

## 📹 Demo

<!-- Add a screenshot or video showcasing your work -->
  • Loading branch information
indietyp authored Feb 10, 2023
1 parent aa051da commit 94ce735
Show file tree
Hide file tree
Showing 8 changed files with 1,059 additions and 0 deletions.
3 changes: 3 additions & 0 deletions libs/deer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
103 changes: 103 additions & 0 deletions libs/deer/src/macros.rs
Original file line number Diff line number Diff line change
@@ -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>()}
};
}
195 changes: 195 additions & 0 deletions libs/deer/src/value.rs
Original file line number Diff line number Diff line change
@@ -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<V>(self, visitor: V) -> error_stack::Result<V::Value, DeserializerError>
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<V>(self, visitor: V) -> error_stack::Result<V::Value, DeserializerError>
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<V>(self, visitor: V) -> error_stack::Result<V::Value, DeserializerError>
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
48 changes: 48 additions & 0 deletions libs/deer/src/value/array.rs
Original file line number Diff line number Diff line change
@@ -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<V>(self, visitor: V) -> Result<V::Value, DeserializerError>
where
V: Visitor<'de>,
{
visitor
.visit_array(self.value)
.change_context(DeserializerError)
}
}
Loading

0 comments on commit 94ce735

Please sign in to comment.