From 8dd9b6e51cd86bd6447b9835236f5c7511e730a7 Mon Sep 17 00:00:00 2001 From: Bilal Mahmoud Date: Sat, 15 Apr 2023 18:29:56 +0200 Subject: [PATCH 01/12] feat: start content enum --- libs/deer/src/content.rs | 8 ++++++++ libs/deer/src/lib.rs | 1 + 2 files changed, 9 insertions(+) create mode 100644 libs/deer/src/content.rs diff --git a/libs/deer/src/content.rs b/libs/deer/src/content.rs new file mode 100644 index 00000000000..837bafc76a5 --- /dev/null +++ b/libs/deer/src/content.rs @@ -0,0 +1,8 @@ +use crate::Number; + +#[derive(Debug, Clone)] +pub enum Content<'de> { + Bool(bool), + + Number(Number), +} diff --git a/libs/deer/src/lib.rs b/libs/deer/src/lib.rs index 7f6d1745d9a..b87dbca1dce 100644 --- a/libs/deer/src/lib.rs +++ b/libs/deer/src/lib.rs @@ -38,6 +38,7 @@ pub mod error; mod impls; #[macro_use] mod macros; +mod content; mod number; pub mod schema; pub mod value; From 8db1dc6161fbdf31dc1a14304a7abb9ea677ffcb Mon Sep 17 00:00:00 2001 From: Bilal Mahmoud Date: Sun, 16 Apr 2023 23:14:51 +0200 Subject: [PATCH 02/12] feat: implement `Visitor` for `Content` --- libs/deer/src/content.rs | 190 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 189 insertions(+), 1 deletion(-) diff --git a/libs/deer/src/content.rs b/libs/deer/src/content.rs index 837bafc76a5..31c24fa03c3 100644 --- a/libs/deer/src/content.rs +++ b/libs/deer/src/content.rs @@ -1,8 +1,196 @@ -use crate::Number; +use alloc::{borrow::ToOwned, string::String, vec::Vec}; + +use error_stack::{Result, ResultExt}; + +use crate::{ + error::{ArrayAccessError, DeserializeError, ObjectAccessError, VisitorError}, + ArrayAccess, Deserialize, Deserializer, Document, Number, ObjectAccess, Visitor, +}; + +mod size_hint { + use core::cmp; + + pub(crate) fn cautious(hint: Option) -> usize { + cmp::min(hint.unwrap_or(0), 4096) + } +} #[derive(Debug, Clone)] pub enum Content<'de> { Bool(bool), Number(Number), + I128(i128), + U128(u128), + + Char(char), + String(String), + Str(&'de str), + + ByteBuf(Vec), + Bytes(&'de [u8]), + + Null, + None, + + Array(Vec>), + Object(Vec<(Content<'de>, Content<'de>)>), +} + +pub struct ContentVisitor; + +impl<'de> Visitor<'de> for ContentVisitor { + type Value = Content<'de>; + + fn expecting(&self) -> Document { + todo!() + } + + fn visit_none(self) -> Result { + Ok(Content::None) + } + + fn visit_null(self) -> Result { + Ok(Content::Null) + } + + fn visit_bool(self, v: bool) -> Result { + Ok(Content::Bool(v)) + } + + fn visit_number(self, v: Number) -> Result { + Ok(Content::Number(v)) + } + + fn visit_char(self, v: char) -> Result { + Ok(Content::Char(v)) + } + + fn visit_str(self, v: &str) -> Result { + Ok(Content::String(v.to_owned())) + } + + fn visit_borrowed_str(self, v: &'de str) -> Result { + Ok(Content::Str(v)) + } + + fn visit_string(self, v: String) -> Result { + Ok(Content::String(v)) + } + + fn visit_bytes(self, v: &[u8]) -> Result { + Ok(Content::ByteBuf(v.to_vec())) + } + + fn visit_borrowed_bytes(self, v: &'de [u8]) -> Result { + Ok(Content::Bytes(v)) + } + + fn visit_bytes_buffer(self, v: Vec) -> Result { + Ok(Content::ByteBuf(v)) + } + + fn visit_array(self, mut v: T) -> Result + where + T: ArrayAccess<'de>, + { + let mut array = Vec::with_capacity(size_hint::cautious(v.size_hint())); + let mut errors: Result<(), ArrayAccessError> = Ok(()); + + while let Some(entry) = v.next() { + match (entry, &mut errors) { + (Ok(entry), Ok(_)) => array.push(entry), + // we don't need to allocate if we already have an error + (Ok(_), Err(_)) => {} + (Err(error), Err(errors)) => { + errors.extend_one(error); + } + (Err(error), errors) => *errors = Err(error), + } + } + + Ok(Content::Array(array)) + } + + fn visit_object(self, mut v: T) -> Result + where + T: ObjectAccess<'de>, + { + let mut object = Vec::with_capacity(size_hint::cautious(v.size_hint())); + let mut errors: Result<(), ObjectAccessError> = Ok(()); + + while let Some(entry) = v.next() { + match (entry, &mut errors) { + (Ok(entry), Ok(_)) => object.push(entry), + // we don't need to allocate if we already have an error + (Ok(_), Err(_)) => {} + (Err(error), Err(errors)) => { + errors.extend_one(error); + } + (Err(error), errors) => *errors = Err(error), + } + } + + Ok(Content::Object(object)) + } + + fn visit_i8(self, v: i8) -> Result { + Ok(Content::Number(v.into())) + } + + fn visit_i16(self, v: i16) -> Result { + Ok(Content::Number(v.into())) + } + + fn visit_i32(self, v: i32) -> Result { + Ok(Content::Number(v.into())) + } + + fn visit_i64(self, v: i64) -> Result { + Ok(Content::Number(v.into())) + } + + fn visit_i128(self, v: i128) -> Result { + Ok(Content::I128(v)) + } + + fn visit_u8(self, v: u8) -> Result { + Ok(Content::Number(v.into())) + } + + fn visit_u16(self, v: u16) -> Result { + Ok(Content::Number(v.into())) + } + + fn visit_u32(self, v: u32) -> Result { + Ok(Content::Number(v.into())) + } + + fn visit_u64(self, v: u64) -> Result { + Ok(Content::Number(v.into())) + } + + fn visit_u128(self, v: u128) -> Result { + Ok(Content::U128(v)) + } + + fn visit_f32(self, v: f32) -> Result { + Ok(Content::Number(v.into())) + } + + fn visit_f64(self, v: f64) -> Result { + Ok(Content::Number(v.into())) + } } + +impl<'de> Deserialize<'de> for Content<'de> { + type Reflection = (); + + fn deserialize>(de: D) -> Result { + de.deserialize_any(ContentVisitor) + .change_context(DeserializeError) + } +} + +// TODO: to shortcircuit `Content` (if deserialized multiple times) serde actually uses a +// `__deserialize_content` From 000303db3d6ac953a7514b7d1dbd5d44b4aac98f Mon Sep 17 00:00:00 2001 From: Bilal Mahmoud Date: Mon, 17 Apr 2023 10:04:56 +0200 Subject: [PATCH 03/12] feat: outline deserialization logic --- libs/deer/src/content.rs | 254 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 251 insertions(+), 3 deletions(-) diff --git a/libs/deer/src/content.rs b/libs/deer/src/content.rs index 31c24fa03c3..784564a598e 100644 --- a/libs/deer/src/content.rs +++ b/libs/deer/src/content.rs @@ -1,10 +1,14 @@ use alloc::{borrow::ToOwned, string::String, vec::Vec}; -use error_stack::{Result, ResultExt}; +use error_stack::{Report, Result, ResultExt}; use crate::{ - error::{ArrayAccessError, DeserializeError, ObjectAccessError, VisitorError}, - ArrayAccess, Deserialize, Deserializer, Document, Number, ObjectAccess, Visitor, + error::{ + ArrayAccessError, DeserializeError, DeserializerError, Error, ExpectedType, MissingError, + ObjectAccessError, ReceivedValue, ValueError, Variant, VisitorError, + }, + ArrayAccess, Context, Deserialize, Deserializer, Document, EnumVisitor, Number, ObjectAccess, + OptionalVisitor, Reflection, Visitor, }; mod size_hint { @@ -192,5 +196,249 @@ impl<'de> Deserialize<'de> for Content<'de> { } } +// TODO: OptionalVisitor +// TODO: EnumVisitor +// TODO: FieldVisitor (?) +// TODO: Reflection c: + // TODO: to shortcircuit `Content` (if deserialized multiple times) serde actually uses a // `__deserialize_content` + +fn invalid_type(received: Content) -> Report { + let received = match received { + Content::Bool(value) => Some(ReceivedValue::new(value)), + Content::Number(value) => Some(ReceivedValue::new(value)), + Content::I128(value) => Some(ReceivedValue::new(value)), + Content::U128(value) => Some(ReceivedValue::new(value)), + Content::Char(value) => Some(ReceivedValue::new(value)), + Content::String(value) => Some(ReceivedValue::new(value)), + Content::Str(value) => Some(ReceivedValue::new(value.to_owned())), + Content::ByteBuf(value) => Some(ReceivedValue::new(value)), + Content::Bytes(value) => Some(ReceivedValue::new(value.to_vec())), + Content::Null => Some(ReceivedValue::new(())), + Content::None => None, + // TODO: we need to "remove" `None` from the serialization + Content::Array(value) => todo!(), + Content::Object(value) => todo!(), + }; + + let expected = ExpectedType::new(T::document()); + + match received { + None => Report::new(MissingError.into_error()).attach(expected), + Some(received) => Report::new(ValueError.into_error()) + .attach(expected) + .attach(received), + } +} + +struct ContentDeserializer<'de> { + content: Content<'de>, + context: Context, +} + +impl<'de> ContentDeserializer<'de> { + pub fn new(content: Content<'de>, context: Context) -> Self { + Self { content, context } + } +} + +impl<'de> Deserializer<'de> for ContentDeserializer<'de> { + fn context(&self) -> &Context { + &self.context + } + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.content { + Content::Bool(value) => visitor.visit_bool(value), + Content::Number(value) => visitor.visit_number(value), + Content::I128(value) => visitor.visit_i128(value), + Content::U128(value) => visitor.visit_u128(value), + Content::Char(value) => visitor.visit_char(value), + Content::String(value) => visitor.visit_string(value), + Content::Str(value) => visitor.visit_borrowed_str(value), + Content::ByteBuf(value) => visitor.visit_bytes_buffer(value), + Content::Bytes(value) => visitor.visit_borrowed_bytes(value), + Content::Null => visitor.visit_null(), + Content::None => visitor.visit_none(), + Content::Array(_) => todo!(), // SeqIterator + Content::Object(_) => todo!(), // SeqIterator + } + .change_context(DeserializerError) + } + + fn deserialize_null(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.content { + Content::Null => visitor.visit_null().change_context(DeserializerError), + received => Err(invalid_type::<<() as Deserialize>::Reflection>(received) + .change_context(DeserializerError)), + } + } + + fn deserialize_bool(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.content { + Content::Bool(value) => visitor.visit_bool(value).change_context(DeserializerError), + received => Err(invalid_type::(received).change_context(DeserializerError)), + } + } + + fn deserialize_number(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.content { + Content::Number(value) => visitor + .visit_number(value) + .change_context(DeserializerError), + received => Err(invalid_type::(received).change_context(DeserializerError)), + } + } + + fn deserialize_char(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.content { + Content::Char(value) => visitor.visit_char(value).change_context(DeserializerError), + Content::String(value) => visitor + .visit_string(value) + .change_context(DeserializerError), + Content::Str(value) => visitor + .visit_borrowed_str(value) + .change_context(DeserializerError), + received => Err(invalid_type::(received).change_context(DeserializerError)), + } + } + + fn deserialize_string(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_str(visitor) + } + + fn deserialize_str(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.content { + Content::String(value) => visitor + .visit_str(value.as_str()) + .change_context(DeserializerError), + Content::Str(value) => visitor + .visit_borrowed_str(value) + .change_context(DeserializerError), + Content::ByteBuf(value) => visitor + .visit_bytes_buffer(value) + .change_context(DeserializerError), + Content::Bytes(value) => visitor + .visit_borrowed_bytes(value) + .change_context(DeserializerError), + received => Err(invalid_type::(received).change_context(DeserializerError)), + } + } + + fn deserialize_bytes(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.content { + Content::String(value) => visitor + .visit_str(value.as_str()) + .change_context(DeserializerError), + Content::Str(value) => visitor + .visit_borrowed_str(value) + .change_context(DeserializerError), + Content::ByteBuf(value) => visitor + .visit_bytes_buffer(value) + .change_context(DeserializerError), + Content::Bytes(value) => visitor + .visit_borrowed_bytes(value) + .change_context(DeserializerError), + Content::Array(value) => todo!(), + received => Err(invalid_type::(received).change_context(DeserializerError)), + } + } + + fn deserialize_bytes_buffer(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_bytes(visitor) + } + + fn deserialize_array(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!() + } + + fn deserialize_object(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!() + } + + fn deserialize_optional(self, visitor: V) -> Result + where + V: OptionalVisitor<'de>, + { + match self.content { + Content::Null => visitor.visit_null(), + Content::None => visitor.visit_none(), + other => visitor.visit_some(ContentDeserializer::new(other, self.context)), + } + .change_context(DeserializerError) + } + + fn deserialize_enum(self, visitor: V) -> Result + where + V: EnumVisitor<'de>, + { + let errors: Result<(), DeserializerError> = Ok(()); + + let (discriminant, value) = match self.content { + Content::Object(value) => { + // TODO: ensure map with a single key + let length = value.len(); + + match length { + 0 => (Content::None, Content::None), + 1 => value[0], + // TODO: add to errors + n => value[0], + } + } + other => (other, Content::None), + }; + + let context = self.context; + + let discriminant = + visitor.visit_discriminant(ContentDeserializer::new(discriminant, context))?; + + // todo: add to errors, but abort if any errors in discriminant + + let value = visitor.visit_value(discriminant, ContentDeserializer::new(value, context))?; + + Ok(value) + } +} + +struct ContentRefDeserializer<'a, 'de: 'a> { + content: &'a Content<'de>, + context: Context, +} + +impl<'de> Deserializer<'de> for ContentRefDeserializer<'_, 'de> {} From 5016f3783b0e7ebe882a5d6ab453f1226d27a681 Mon Sep 17 00:00:00 2001 From: Bilal Mahmoud Date: Mon, 17 Apr 2023 10:11:30 +0200 Subject: [PATCH 04/12] feat: work on `deserialize_enum` --- libs/deer/src/content.rs | 46 +++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/libs/deer/src/content.rs b/libs/deer/src/content.rs index 784564a598e..bec4f37ff4c 100644 --- a/libs/deer/src/content.rs +++ b/libs/deer/src/content.rs @@ -5,7 +5,7 @@ use error_stack::{Report, Result, ResultExt}; use crate::{ error::{ ArrayAccessError, DeserializeError, DeserializerError, Error, ExpectedType, MissingError, - ObjectAccessError, ReceivedValue, ValueError, Variant, VisitorError, + ObjectAccessError, ReceivedValue, TypeError, ValueError, Variant, VisitorError, }, ArrayAccess, Context, Deserialize, Deserializer, Document, EnumVisitor, Number, ObjectAccess, OptionalVisitor, Reflection, Visitor, @@ -232,18 +232,18 @@ fn invalid_type(received: Content) -> Report { } } -struct ContentDeserializer<'de> { +struct ContentDeserializer<'a, 'de> { content: Content<'de>, - context: Context, + context: &'a mut Context, } -impl<'de> ContentDeserializer<'de> { - pub fn new(content: Content<'de>, context: Context) -> Self { +impl<'a, 'de> ContentDeserializer<'a, 'de> { + pub fn new(content: Content<'de>, context: &'a mut Context) -> Self { Self { content, context } } } -impl<'de> Deserializer<'de> for ContentDeserializer<'de> { +impl<'de> Deserializer<'de> for ContentDeserializer<'_, 'de> { fn context(&self) -> &Context { &self.context } @@ -406,18 +406,23 @@ impl<'de> Deserializer<'de> for ContentDeserializer<'de> { where V: EnumVisitor<'de>, { - let errors: Result<(), DeserializerError> = Ok(()); + let mut errors: Result<(), DeserializerError> = Ok(()); let (discriminant, value) = match self.content { Content::Object(value) => { - // TODO: ensure map with a single key let length = value.len(); match length { 0 => (Content::None, Content::None), 1 => value[0], - // TODO: add to errors - n => value[0], + n => { + // TODO: better error + errors = + Err(Report::new(TypeError.into_error()) + .change_context(DeserializerError)); + + value[0] + } } } other => (other, Content::None), @@ -426,13 +431,24 @@ impl<'de> Deserializer<'de> for ContentDeserializer<'de> { let context = self.context; let discriminant = - visitor.visit_discriminant(ContentDeserializer::new(discriminant, context))?; - - // todo: add to errors, but abort if any errors in discriminant + visitor.visit_discriminant(ContentDeserializer::new(discriminant, context)); - let value = visitor.visit_value(discriminant, ContentDeserializer::new(value, context))?; + match discriminant { + Ok(discriminant) => { + let value = visitor + .visit_value(discriminant, ContentDeserializer::new(value, context)) + .change_context(DeserializerError); - Ok(value) + (value, errors).fold_reports().map(|(value, _)| value) + } + Err(error) => match errors { + Err(mut errors) => { + errors.extend_one(error.change_context(DeserializerError)); + Err(errors) + } + _ => Err(error.change_context(DeserializerError)), + }, + } } } From 56975b2836106beb23a93b575ce99ca775130cab Mon Sep 17 00:00:00 2001 From: Bilal Mahmoud Date: Mon, 17 Apr 2023 18:42:17 +0200 Subject: [PATCH 05/12] feat: start implementation of `ContentRefDeserializer` --- libs/deer/src/content.rs | 99 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 2 deletions(-) diff --git a/libs/deer/src/content.rs b/libs/deer/src/content.rs index bec4f37ff4c..1d7e8c709f3 100644 --- a/libs/deer/src/content.rs +++ b/libs/deer/src/content.rs @@ -454,7 +454,102 @@ impl<'de> Deserializer<'de> for ContentDeserializer<'_, 'de> { struct ContentRefDeserializer<'a, 'de: 'a> { content: &'a Content<'de>, - context: Context, + context: &'a mut Context, } -impl<'de> Deserializer<'de> for ContentRefDeserializer<'_, 'de> {} +impl<'de> Deserializer<'de> for ContentRefDeserializer<'_, 'de> { + fn context(&self) -> &Context { + &self.context + } + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!() + } + + fn deserialize_null(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!() + } + + fn deserialize_bool(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!() + } + + fn deserialize_number(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!() + } + + fn deserialize_char(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!() + } + + fn deserialize_string(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!() + } + + fn deserialize_str(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!() + } + + fn deserialize_bytes(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!() + } + + fn deserialize_bytes_buffer(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!() + } + + fn deserialize_array(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!() + } + + fn deserialize_object(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!() + } + + fn deserialize_optional(self, visitor: V) -> Result + where + V: OptionalVisitor<'de>, + { + todo!() + } + + fn deserialize_enum(self, visitor: V) -> Result + where + V: EnumVisitor<'de>, + { + todo!() + } +} From 52fed4782906affcf08fe4a1362d404313675558 Mon Sep 17 00:00:00 2001 From: Bilal Mahmoud Date: Wed, 3 May 2023 10:19:28 +0200 Subject: [PATCH 06/12] feat: add internal `__deserialize_content` --- libs/deer/src/content.rs | 19 +++++++++++++++++++ libs/deer/src/lib.rs | 21 ++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/libs/deer/src/content.rs b/libs/deer/src/content.rs index 1d7e8c709f3..d8585306648 100644 --- a/libs/deer/src/content.rs +++ b/libs/deer/src/content.rs @@ -7,6 +7,7 @@ use crate::{ ArrayAccessError, DeserializeError, DeserializerError, Error, ExpectedType, MissingError, ObjectAccessError, ReceivedValue, TypeError, ValueError, Variant, VisitorError, }, + sealed::T, ArrayAccess, Context, Deserialize, Deserializer, Document, EnumVisitor, Number, ObjectAccess, OptionalVisitor, Reflection, Visitor, }; @@ -450,6 +451,15 @@ impl<'de> Deserializer<'de> for ContentDeserializer<'_, 'de> { }, } } + + fn __deserialize_content(self, _: T, visitor: V) -> Result, DeserializerError> + where + V: Visitor<'de, Value = Content<'de>>, + { + let _ = visitor; + + Ok(self.content) + } } struct ContentRefDeserializer<'a, 'de: 'a> { @@ -552,4 +562,13 @@ impl<'de> Deserializer<'de> for ContentRefDeserializer<'_, 'de> { { todo!() } + + fn __deserialize_content(self, _: T, visitor: V) -> Result, DeserializerError> + where + V: Visitor<'de, Value = Content<'de>>, + { + let _ = visitor; + + Ok(self.content.clone()) + } } diff --git a/libs/deer/src/lib.rs b/libs/deer/src/lib.rs index b87dbca1dce..52bf5c3876a 100644 --- a/libs/deer/src/lib.rs +++ b/libs/deer/src/lib.rs @@ -23,8 +23,8 @@ use error_stack::{Report, Result, ResultExt}; use num_traits::{FromPrimitive, ToPrimitive}; pub use schema::{Document, Reflection, Schema}; -pub use crate::{context::Context, number::Number}; use crate::{ + content::Content, error::{ ArrayAccessError, DeserializeError, DeserializerError, ExpectedType, MissingError, ObjectAccessError, ReceivedType, ReceivedValue, TypeError, ValueError, Variant, @@ -32,6 +32,7 @@ use crate::{ }, schema::visitor, }; +pub use crate::{context::Context, number::Number}; mod context; pub mod error; @@ -45,6 +46,10 @@ pub mod value; extern crate alloc; +pub(crate) mod sealed { + pub struct T; +} + struct GenericFieldVisitor(PhantomData *const (T, U)>); impl<'de, T: Deserialize<'de>, U: Deserialize<'de>> FieldVisitor<'de> @@ -641,6 +646,20 @@ pub trait Deserializer<'de>: Sized { deserialize_f32(to_f32: f32) -> visit_f32, deserialize_f64(to_f64: f64) -> visit_f64, ]; + + // Not public API. + // No stability guarantees are given! + #[doc(hidden)] + fn __deserialize_content( + self, + _: sealed::T, + visitor: V, + ) -> Result, DeserializerError> + where + V: Visitor<'de, Value = Content<'de>>, + { + self.deserialize_any(visitor) + } } /// A **data-structure** that can be deserialized from any format supported by deer. From 6c5863df3762e857a7df086e718d5858a243f763 Mon Sep 17 00:00:00 2001 From: Bilal Mahmoud Date: Wed, 3 May 2023 10:25:18 +0200 Subject: [PATCH 07/12] chore: TODO --- libs/deer/src/content.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/deer/src/content.rs b/libs/deer/src/content.rs index d8585306648..8c9a840376e 100644 --- a/libs/deer/src/content.rs +++ b/libs/deer/src/content.rs @@ -563,6 +563,7 @@ impl<'de> Deserializer<'de> for ContentRefDeserializer<'_, 'de> { todo!() } + // TODO: is there a better way? I don't think without specialization (default impl) fn __deserialize_content(self, _: T, visitor: V) -> Result, DeserializerError> where V: Visitor<'de, Value = Content<'de>>, From 8d0b91327ccf1f5817b0ee690ed776386fb7a542 Mon Sep 17 00:00:00 2001 From: Bilal Mahmoud Date: Wed, 3 May 2023 12:05:01 +0200 Subject: [PATCH 08/12] fix: imports --- libs/deer/src/content.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/deer/src/content.rs b/libs/deer/src/content.rs index 8c9a840376e..7265f382352 100644 --- a/libs/deer/src/content.rs +++ b/libs/deer/src/content.rs @@ -7,6 +7,7 @@ use crate::{ ArrayAccessError, DeserializeError, DeserializerError, Error, ExpectedType, MissingError, ObjectAccessError, ReceivedValue, TypeError, ValueError, Variant, VisitorError, }, + ext::TupleExt, sealed::T, ArrayAccess, Context, Deserialize, Deserializer, Document, EnumVisitor, Number, ObjectAccess, OptionalVisitor, Reflection, Visitor, From f2ce34c96f23a877ae9e22eb4b4b2882c109feb8 Mon Sep 17 00:00:00 2001 From: Bilal Mahmoud Date: Wed, 7 Jun 2023 17:21:00 +0200 Subject: [PATCH 09/12] feat: move `Content` --- libs/deer/src/content.rs | 148 +---------------------------- libs/deer/src/content/visitor.rs | 155 +++++++++++++++++++++++++++++++ libs/deer/src/lib.rs | 20 ++++ 3 files changed, 177 insertions(+), 146 deletions(-) create mode 100644 libs/deer/src/content/visitor.rs diff --git a/libs/deer/src/content.rs b/libs/deer/src/content.rs index 7265f382352..0ba303b1992 100644 --- a/libs/deer/src/content.rs +++ b/libs/deer/src/content.rs @@ -1,3 +1,5 @@ +mod visitor; + use alloc::{borrow::ToOwned, string::String, vec::Vec}; use error_stack::{Report, Result, ResultExt}; @@ -43,152 +45,6 @@ pub enum Content<'de> { Object(Vec<(Content<'de>, Content<'de>)>), } -pub struct ContentVisitor; - -impl<'de> Visitor<'de> for ContentVisitor { - type Value = Content<'de>; - - fn expecting(&self) -> Document { - todo!() - } - - fn visit_none(self) -> Result { - Ok(Content::None) - } - - fn visit_null(self) -> Result { - Ok(Content::Null) - } - - fn visit_bool(self, v: bool) -> Result { - Ok(Content::Bool(v)) - } - - fn visit_number(self, v: Number) -> Result { - Ok(Content::Number(v)) - } - - fn visit_char(self, v: char) -> Result { - Ok(Content::Char(v)) - } - - fn visit_str(self, v: &str) -> Result { - Ok(Content::String(v.to_owned())) - } - - fn visit_borrowed_str(self, v: &'de str) -> Result { - Ok(Content::Str(v)) - } - - fn visit_string(self, v: String) -> Result { - Ok(Content::String(v)) - } - - fn visit_bytes(self, v: &[u8]) -> Result { - Ok(Content::ByteBuf(v.to_vec())) - } - - fn visit_borrowed_bytes(self, v: &'de [u8]) -> Result { - Ok(Content::Bytes(v)) - } - - fn visit_bytes_buffer(self, v: Vec) -> Result { - Ok(Content::ByteBuf(v)) - } - - fn visit_array(self, mut v: T) -> Result - where - T: ArrayAccess<'de>, - { - let mut array = Vec::with_capacity(size_hint::cautious(v.size_hint())); - let mut errors: Result<(), ArrayAccessError> = Ok(()); - - while let Some(entry) = v.next() { - match (entry, &mut errors) { - (Ok(entry), Ok(_)) => array.push(entry), - // we don't need to allocate if we already have an error - (Ok(_), Err(_)) => {} - (Err(error), Err(errors)) => { - errors.extend_one(error); - } - (Err(error), errors) => *errors = Err(error), - } - } - - Ok(Content::Array(array)) - } - - fn visit_object(self, mut v: T) -> Result - where - T: ObjectAccess<'de>, - { - let mut object = Vec::with_capacity(size_hint::cautious(v.size_hint())); - let mut errors: Result<(), ObjectAccessError> = Ok(()); - - while let Some(entry) = v.next() { - match (entry, &mut errors) { - (Ok(entry), Ok(_)) => object.push(entry), - // we don't need to allocate if we already have an error - (Ok(_), Err(_)) => {} - (Err(error), Err(errors)) => { - errors.extend_one(error); - } - (Err(error), errors) => *errors = Err(error), - } - } - - Ok(Content::Object(object)) - } - - fn visit_i8(self, v: i8) -> Result { - Ok(Content::Number(v.into())) - } - - fn visit_i16(self, v: i16) -> Result { - Ok(Content::Number(v.into())) - } - - fn visit_i32(self, v: i32) -> Result { - Ok(Content::Number(v.into())) - } - - fn visit_i64(self, v: i64) -> Result { - Ok(Content::Number(v.into())) - } - - fn visit_i128(self, v: i128) -> Result { - Ok(Content::I128(v)) - } - - fn visit_u8(self, v: u8) -> Result { - Ok(Content::Number(v.into())) - } - - fn visit_u16(self, v: u16) -> Result { - Ok(Content::Number(v.into())) - } - - fn visit_u32(self, v: u32) -> Result { - Ok(Content::Number(v.into())) - } - - fn visit_u64(self, v: u64) -> Result { - Ok(Content::Number(v.into())) - } - - fn visit_u128(self, v: u128) -> Result { - Ok(Content::U128(v)) - } - - fn visit_f32(self, v: f32) -> Result { - Ok(Content::Number(v.into())) - } - - fn visit_f64(self, v: f64) -> Result { - Ok(Content::Number(v.into())) - } -} - impl<'de> Deserialize<'de> for Content<'de> { type Reflection = (); diff --git a/libs/deer/src/content/visitor.rs b/libs/deer/src/content/visitor.rs new file mode 100644 index 00000000000..aa6e373b175 --- /dev/null +++ b/libs/deer/src/content/visitor.rs @@ -0,0 +1,155 @@ +use alloc::vec::Vec; + +use error_stack::Result; + +use crate::{ + content::{size_hint, Content}, + error::{ArrayAccessError, ObjectAccessError, VisitorError}, + ArrayAccess, Document, ObjectAccess, Visitor, +}; + +pub struct ContentVisitor; + +impl<'de> Visitor<'de> for ContentVisitor { + type Value = Content<'de>; + + fn expecting(&self) -> Document { + todo!() + } + + fn visit_none(self) -> Result { + Ok(Content::None) + } + + fn visit_null(self) -> Result { + Ok(Content::Null) + } + + fn visit_bool(self, v: bool) -> Result { + Ok(Content::Bool(v)) + } + + fn visit_number(self, v: Number) -> Result { + Ok(Content::Number(v)) + } + + fn visit_char(self, v: char) -> Result { + Ok(Content::Char(v)) + } + + fn visit_str(self, v: &str) -> Result { + Ok(Content::String(v.to_owned())) + } + + fn visit_borrowed_str(self, v: &'de str) -> Result { + Ok(Content::Str(v)) + } + + fn visit_string(self, v: String) -> Result { + Ok(Content::String(v)) + } + + fn visit_bytes(self, v: &[u8]) -> Result { + Ok(Content::ByteBuf(v.to_vec())) + } + + fn visit_borrowed_bytes(self, v: &'de [u8]) -> Result { + Ok(Content::Bytes(v)) + } + + fn visit_bytes_buffer(self, v: Vec) -> Result { + Ok(Content::ByteBuf(v)) + } + + fn visit_array(self, mut v: T) -> Result + where + T: ArrayAccess<'de>, + { + let mut array = Vec::with_capacity(size_hint::cautious(v.size_hint())); + let mut errors: Result<(), ArrayAccessError> = Ok(()); + + while let Some(entry) = v.next() { + match (entry, &mut errors) { + (Ok(entry), Ok(_)) => array.push(entry), + // we don't need to allocate if we already have an error + (Ok(_), Err(_)) => {} + (Err(error), Err(errors)) => { + errors.extend_one(error); + } + (Err(error), errors) => *errors = Err(error), + } + } + + Ok(Content::Array(array)) + } + + fn visit_object(self, mut v: T) -> Result + where + T: ObjectAccess<'de>, + { + let mut object = Vec::with_capacity(size_hint::cautious(v.size_hint())); + let mut errors: Result<(), ObjectAccessError> = Ok(()); + + while let Some(entry) = v.next() { + match (entry, &mut errors) { + (Ok(entry), Ok(_)) => object.push(entry), + // we don't need to allocate if we already have an error + (Ok(_), Err(_)) => {} + (Err(error), Err(errors)) => { + errors.extend_one(error); + } + (Err(error), errors) => *errors = Err(error), + } + } + + Ok(Content::Object(object)) + } + + fn visit_i8(self, v: i8) -> Result { + Ok(Content::Number(v.into())) + } + + fn visit_i16(self, v: i16) -> Result { + Ok(Content::Number(v.into())) + } + + fn visit_i32(self, v: i32) -> Result { + Ok(Content::Number(v.into())) + } + + fn visit_i64(self, v: i64) -> Result { + Ok(Content::Number(v.into())) + } + + fn visit_i128(self, v: i128) -> Result { + Ok(Content::I128(v)) + } + + fn visit_u8(self, v: u8) -> Result { + Ok(Content::Number(v.into())) + } + + fn visit_u16(self, v: u16) -> Result { + Ok(Content::Number(v.into())) + } + + fn visit_u32(self, v: u32) -> Result { + Ok(Content::Number(v.into())) + } + + fn visit_u64(self, v: u64) -> Result { + Ok(Content::Number(v.into())) + } + + fn visit_u128(self, v: u128) -> Result { + Ok(Content::U128(v)) + } + + fn visit_f32(self, v: f32) -> Result { + Ok(Content::Number(v.into())) + } + + fn visit_f64(self, v: f64) -> Result { + Ok(Content::Number(v.into())) + } +} diff --git a/libs/deer/src/lib.rs b/libs/deer/src/lib.rs index ceeb3da7aaf..bf3279f4408 100644 --- a/libs/deer/src/lib.rs +++ b/libs/deer/src/lib.rs @@ -25,6 +25,7 @@ pub use schema::{Document, Reflection, Schema}; use crate::{ bound::{BoundArrayAccess, BoundObjectAccess}, + content::Content, error::{ ArrayAccessError, BoundedContractViolationError, DeserializeError, DeserializerError, ExpectedType, MissingError, ObjectAccessError, ReceivedType, ReceivedValue, TypeError, @@ -40,6 +41,7 @@ mod impls; #[macro_use] mod macros; mod bound; +pub mod content; mod ext; pub mod helpers; mod number; @@ -52,6 +54,10 @@ pub mod export { pub use error_stack; } +pub(crate) mod sealed { + pub struct T; +} + struct GenericFieldVisitor(PhantomData *const (T, U)>); impl<'de, T: Deserialize<'de>, U: Deserialize<'de>> FieldVisitor<'de> @@ -751,6 +757,20 @@ pub trait Deserializer<'de>: Sized { fn deserialize_identifier(self, visitor: V) -> Result where V: IdentifierVisitor<'de>; + + // Not public API. + // No stability guarantees are given! + #[doc(hidden)] + fn __deserialize_content( + self, + _: sealed::T, + visitor: V, + ) -> Result, DeserializerError> + where + V: Visitor<'de, Value = Content<'de>>, + { + self.deserialize_any(visitor) + } } /// A **data-structure** that can be deserialized from any format supported by deer. From 116fc9668ef5227b7935b851af2289beaec6c2c6 Mon Sep 17 00:00:00 2001 From: Bilal Mahmoud Date: Wed, 7 Jun 2023 17:40:17 +0200 Subject: [PATCH 10/12] feat: split content --- libs/deer/src/content.rs | 344 +--------------------- libs/deer/src/content/deserializer.rs | 255 ++++++++++++++++ libs/deer/src/content/deserializer_ref.rs | 132 +++++++++ libs/deer/src/content/visitor.rs | 4 +- 4 files changed, 392 insertions(+), 343 deletions(-) create mode 100644 libs/deer/src/content/deserializer.rs create mode 100644 libs/deer/src/content/deserializer_ref.rs diff --git a/libs/deer/src/content.rs b/libs/deer/src/content.rs index 0ba303b1992..c18c776714d 100644 --- a/libs/deer/src/content.rs +++ b/libs/deer/src/content.rs @@ -1,3 +1,5 @@ +mod deserializer; +mod deserializer_ref; mod visitor; use alloc::{borrow::ToOwned, string::String, vec::Vec}; @@ -5,6 +7,7 @@ use alloc::{borrow::ToOwned, string::String, vec::Vec}; use error_stack::{Report, Result, ResultExt}; use crate::{ + content::visitor::ContentVisitor, error::{ ArrayAccessError, DeserializeError, DeserializerError, Error, ExpectedType, MissingError, ObjectAccessError, ReceivedValue, TypeError, ValueError, Variant, VisitorError, @@ -89,344 +92,3 @@ fn invalid_type(received: Content) -> Report { .attach(received), } } - -struct ContentDeserializer<'a, 'de> { - content: Content<'de>, - context: &'a mut Context, -} - -impl<'a, 'de> ContentDeserializer<'a, 'de> { - pub fn new(content: Content<'de>, context: &'a mut Context) -> Self { - Self { content, context } - } -} - -impl<'de> Deserializer<'de> for ContentDeserializer<'_, 'de> { - fn context(&self) -> &Context { - &self.context - } - - fn deserialize_any(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self.content { - Content::Bool(value) => visitor.visit_bool(value), - Content::Number(value) => visitor.visit_number(value), - Content::I128(value) => visitor.visit_i128(value), - Content::U128(value) => visitor.visit_u128(value), - Content::Char(value) => visitor.visit_char(value), - Content::String(value) => visitor.visit_string(value), - Content::Str(value) => visitor.visit_borrowed_str(value), - Content::ByteBuf(value) => visitor.visit_bytes_buffer(value), - Content::Bytes(value) => visitor.visit_borrowed_bytes(value), - Content::Null => visitor.visit_null(), - Content::None => visitor.visit_none(), - Content::Array(_) => todo!(), // SeqIterator - Content::Object(_) => todo!(), // SeqIterator - } - .change_context(DeserializerError) - } - - fn deserialize_null(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self.content { - Content::Null => visitor.visit_null().change_context(DeserializerError), - received => Err(invalid_type::<<() as Deserialize>::Reflection>(received) - .change_context(DeserializerError)), - } - } - - fn deserialize_bool(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self.content { - Content::Bool(value) => visitor.visit_bool(value).change_context(DeserializerError), - received => Err(invalid_type::(received).change_context(DeserializerError)), - } - } - - fn deserialize_number(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self.content { - Content::Number(value) => visitor - .visit_number(value) - .change_context(DeserializerError), - received => Err(invalid_type::(received).change_context(DeserializerError)), - } - } - - fn deserialize_char(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self.content { - Content::Char(value) => visitor.visit_char(value).change_context(DeserializerError), - Content::String(value) => visitor - .visit_string(value) - .change_context(DeserializerError), - Content::Str(value) => visitor - .visit_borrowed_str(value) - .change_context(DeserializerError), - received => Err(invalid_type::(received).change_context(DeserializerError)), - } - } - - fn deserialize_string(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_str(visitor) - } - - fn deserialize_str(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self.content { - Content::String(value) => visitor - .visit_str(value.as_str()) - .change_context(DeserializerError), - Content::Str(value) => visitor - .visit_borrowed_str(value) - .change_context(DeserializerError), - Content::ByteBuf(value) => visitor - .visit_bytes_buffer(value) - .change_context(DeserializerError), - Content::Bytes(value) => visitor - .visit_borrowed_bytes(value) - .change_context(DeserializerError), - received => Err(invalid_type::(received).change_context(DeserializerError)), - } - } - - fn deserialize_bytes(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self.content { - Content::String(value) => visitor - .visit_str(value.as_str()) - .change_context(DeserializerError), - Content::Str(value) => visitor - .visit_borrowed_str(value) - .change_context(DeserializerError), - Content::ByteBuf(value) => visitor - .visit_bytes_buffer(value) - .change_context(DeserializerError), - Content::Bytes(value) => visitor - .visit_borrowed_bytes(value) - .change_context(DeserializerError), - Content::Array(value) => todo!(), - received => Err(invalid_type::(received).change_context(DeserializerError)), - } - } - - fn deserialize_bytes_buffer(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_bytes(visitor) - } - - fn deserialize_array(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - todo!() - } - - fn deserialize_object(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - todo!() - } - - fn deserialize_optional(self, visitor: V) -> Result - where - V: OptionalVisitor<'de>, - { - match self.content { - Content::Null => visitor.visit_null(), - Content::None => visitor.visit_none(), - other => visitor.visit_some(ContentDeserializer::new(other, self.context)), - } - .change_context(DeserializerError) - } - - fn deserialize_enum(self, visitor: V) -> Result - where - V: EnumVisitor<'de>, - { - let mut errors: Result<(), DeserializerError> = Ok(()); - - let (discriminant, value) = match self.content { - Content::Object(value) => { - let length = value.len(); - - match length { - 0 => (Content::None, Content::None), - 1 => value[0], - n => { - // TODO: better error - errors = - Err(Report::new(TypeError.into_error()) - .change_context(DeserializerError)); - - value[0] - } - } - } - other => (other, Content::None), - }; - - let context = self.context; - - let discriminant = - visitor.visit_discriminant(ContentDeserializer::new(discriminant, context)); - - match discriminant { - Ok(discriminant) => { - let value = visitor - .visit_value(discriminant, ContentDeserializer::new(value, context)) - .change_context(DeserializerError); - - (value, errors).fold_reports().map(|(value, _)| value) - } - Err(error) => match errors { - Err(mut errors) => { - errors.extend_one(error.change_context(DeserializerError)); - Err(errors) - } - _ => Err(error.change_context(DeserializerError)), - }, - } - } - - fn __deserialize_content(self, _: T, visitor: V) -> Result, DeserializerError> - where - V: Visitor<'de, Value = Content<'de>>, - { - let _ = visitor; - - Ok(self.content) - } -} - -struct ContentRefDeserializer<'a, 'de: 'a> { - content: &'a Content<'de>, - context: &'a mut Context, -} - -impl<'de> Deserializer<'de> for ContentRefDeserializer<'_, 'de> { - fn context(&self) -> &Context { - &self.context - } - - fn deserialize_any(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - todo!() - } - - fn deserialize_null(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - todo!() - } - - fn deserialize_bool(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - todo!() - } - - fn deserialize_number(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - todo!() - } - - fn deserialize_char(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - todo!() - } - - fn deserialize_string(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - todo!() - } - - fn deserialize_str(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - todo!() - } - - fn deserialize_bytes(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - todo!() - } - - fn deserialize_bytes_buffer(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - todo!() - } - - fn deserialize_array(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - todo!() - } - - fn deserialize_object(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - todo!() - } - - fn deserialize_optional(self, visitor: V) -> Result - where - V: OptionalVisitor<'de>, - { - todo!() - } - - fn deserialize_enum(self, visitor: V) -> Result - where - V: EnumVisitor<'de>, - { - todo!() - } - - // TODO: is there a better way? I don't think without specialization (default impl) - fn __deserialize_content(self, _: T, visitor: V) -> Result, DeserializerError> - where - V: Visitor<'de, Value = Content<'de>>, - { - let _ = visitor; - - Ok(self.content.clone()) - } -} diff --git a/libs/deer/src/content/deserializer.rs b/libs/deer/src/content/deserializer.rs new file mode 100644 index 00000000000..7d8fa9bbfde --- /dev/null +++ b/libs/deer/src/content/deserializer.rs @@ -0,0 +1,255 @@ +use error_stack::{Report, Result, ResultExt}; + +use crate::{ + content::{invalid_type, Content}, + error::{DeserializerError, TypeError, Variant}, + ext::TupleExt, + sealed::T, + Context, Deserialize, Deserializer, EnumVisitor, IdentifierVisitor, Number, OptionalVisitor, + StructVisitor, Visitor, +}; + +struct ContentDeserializer<'a, 'de> { + content: Content<'de>, + context: &'a mut Context, +} + +impl<'a, 'de> ContentDeserializer<'a, 'de> { + // Reason: `content` and `context` are the correct names. There are no alternatives. + #[allow(clippy::similar_names)] + pub fn new(content: Content<'de>, context: &'a mut Context) -> Self { + Self { content, context } + } +} + +impl<'de> Deserializer<'de> for ContentDeserializer<'_, 'de> { + fn context(&self) -> &Context { + &self.context + } + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.content { + Content::Bool(value) => visitor.visit_bool(value), + Content::Number(value) => visitor.visit_number(value), + Content::I128(value) => visitor.visit_i128(value), + Content::U128(value) => visitor.visit_u128(value), + Content::Char(value) => visitor.visit_char(value), + Content::String(value) => visitor.visit_string(value), + Content::Str(value) => visitor.visit_borrowed_str(value), + Content::ByteBuf(value) => visitor.visit_bytes_buffer(value), + Content::Bytes(value) => visitor.visit_borrowed_bytes(value), + Content::Null => visitor.visit_null(), + Content::None => visitor.visit_none(), + Content::Array(_) => todo!(), // SeqIterator + Content::Object(_) => todo!(), // SeqIterator + } + .change_context(DeserializerError) + } + + fn deserialize_null(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.content { + Content::Null => visitor.visit_null().change_context(DeserializerError), + received => Err(invalid_type::<<() as Deserialize>::Reflection>(received) + .change_context(DeserializerError)), + } + } + + fn deserialize_bool(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.content { + Content::Bool(value) => visitor.visit_bool(value).change_context(DeserializerError), + received => Err(invalid_type::(received).change_context(DeserializerError)), + } + } + + fn deserialize_number(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.content { + Content::Number(value) => visitor + .visit_number(value) + .change_context(DeserializerError), + received => Err(invalid_type::(received).change_context(DeserializerError)), + } + } + + fn deserialize_char(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.content { + Content::Char(value) => visitor.visit_char(value).change_context(DeserializerError), + Content::String(value) => visitor + .visit_string(value) + .change_context(DeserializerError), + Content::Str(value) => visitor + .visit_borrowed_str(value) + .change_context(DeserializerError), + received => Err(invalid_type::(received).change_context(DeserializerError)), + } + } + + fn deserialize_string(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_str(visitor) + } + + fn deserialize_str(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.content { + Content::String(value) => visitor + .visit_str(value.as_str()) + .change_context(DeserializerError), + Content::Str(value) => visitor + .visit_borrowed_str(value) + .change_context(DeserializerError), + Content::ByteBuf(value) => visitor + .visit_bytes_buffer(value) + .change_context(DeserializerError), + Content::Bytes(value) => visitor + .visit_borrowed_bytes(value) + .change_context(DeserializerError), + received => Err(invalid_type::(received).change_context(DeserializerError)), + } + } + + fn deserialize_bytes(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.content { + Content::String(value) => visitor + .visit_str(value.as_str()) + .change_context(DeserializerError), + Content::Str(value) => visitor + .visit_borrowed_str(value) + .change_context(DeserializerError), + Content::ByteBuf(value) => visitor + .visit_bytes_buffer(value) + .change_context(DeserializerError), + Content::Bytes(value) => visitor + .visit_borrowed_bytes(value) + .change_context(DeserializerError), + Content::Array(value) => todo!(), + received => Err(invalid_type::(received).change_context(DeserializerError)), + } + } + + fn deserialize_bytes_buffer(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_bytes(visitor) + } + + fn deserialize_array(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!() + } + + fn deserialize_object(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!() + } + + fn deserialize_optional(self, visitor: V) -> Result + where + V: OptionalVisitor<'de>, + { + match self.content { + Content::Null => visitor.visit_null(), + Content::None => visitor.visit_none(), + other => visitor.visit_some(ContentDeserializer::new(other, self.context)), + } + .change_context(DeserializerError) + } + + fn deserialize_enum(self, visitor: V) -> Result + where + V: EnumVisitor<'de>, + { + let mut errors: Result<(), DeserializerError> = Ok(()); + + let (discriminant, value) = match self.content { + Content::Object(value) => { + let length = value.len(); + + match length { + 0 => (Content::None, Content::None), + 1 => value[0], + n => { + // TODO: better error + errors = + Err(Report::new(TypeError.into_error()) + .change_context(DeserializerError)); + + value[0] + } + } + } + other => (other, Content::None), + }; + + let context = self.context; + + let discriminant = + visitor.visit_discriminant(ContentDeserializer::new(discriminant, context)); + + match discriminant { + Ok(discriminant) => { + let value = visitor + .visit_value(discriminant, ContentDeserializer::new(value, context)) + .change_context(DeserializerError); + + (value, errors).fold_reports().map(|(value, _)| value) + } + Err(error) => match errors { + Err(mut errors) => { + errors.extend_one(error.change_context(DeserializerError)); + Err(errors) + } + _ => Err(error.change_context(DeserializerError)), + }, + } + } + + fn deserialize_struct(self, visitor: V) -> Result + where + V: StructVisitor<'de>, + { + todo!() + } + + fn deserialize_identifier(self, visitor: V) -> Result + where + V: IdentifierVisitor<'de>, + { + todo!() + } + + fn __deserialize_content(self, _: T, visitor: V) -> Result, DeserializerError> + where + V: Visitor<'de, Value = Content<'de>>, + { + let _ = visitor; + + Ok(self.content) + } +} diff --git a/libs/deer/src/content/deserializer_ref.rs b/libs/deer/src/content/deserializer_ref.rs new file mode 100644 index 00000000000..009caa01f2d --- /dev/null +++ b/libs/deer/src/content/deserializer_ref.rs @@ -0,0 +1,132 @@ +use error_stack::Result; + +use crate::{ + content::Content, error::DeserializerError, sealed::T, Context, Deserializer, EnumVisitor, + IdentifierVisitor, OptionalVisitor, StructVisitor, Visitor, +}; + +struct ContentRefDeserializer<'a, 'de: 'a> { + content: &'a Content<'de>, + context: &'a mut Context, +} + +impl<'de> Deserializer<'de> for ContentRefDeserializer<'_, 'de> { + fn context(&self) -> &Context { + &self.context + } + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!() + } + + fn deserialize_null(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!() + } + + fn deserialize_bool(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!() + } + + fn deserialize_number(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!() + } + + fn deserialize_char(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!() + } + + fn deserialize_string(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!() + } + + fn deserialize_str(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!() + } + + fn deserialize_bytes(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!() + } + + fn deserialize_bytes_buffer(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!() + } + + fn deserialize_array(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!() + } + + fn deserialize_object(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!() + } + + fn deserialize_optional(self, visitor: V) -> Result + where + V: OptionalVisitor<'de>, + { + todo!() + } + + fn deserialize_enum(self, visitor: V) -> Result + where + V: EnumVisitor<'de>, + { + todo!() + } + + fn deserialize_struct(self, visitor: V) -> Result + where + V: StructVisitor<'de>, + { + todo!() + } + + fn deserialize_identifier(self, visitor: V) -> Result + where + V: IdentifierVisitor<'de>, + { + todo!() + } + + // TODO: is there a better way? I don't think without specialization (default impl) + fn __deserialize_content(self, _: T, visitor: V) -> Result, DeserializerError> + where + V: Visitor<'de, Value = Content<'de>>, + { + let _ = visitor; + + Ok(self.content.clone()) + } +} diff --git a/libs/deer/src/content/visitor.rs b/libs/deer/src/content/visitor.rs index aa6e373b175..a4985efacee 100644 --- a/libs/deer/src/content/visitor.rs +++ b/libs/deer/src/content/visitor.rs @@ -1,11 +1,11 @@ -use alloc::vec::Vec; +use alloc::{borrow::ToOwned, string::String, vec::Vec}; use error_stack::Result; use crate::{ content::{size_hint, Content}, error::{ArrayAccessError, ObjectAccessError, VisitorError}, - ArrayAccess, Document, ObjectAccess, Visitor, + ArrayAccess, Document, Number, ObjectAccess, Visitor, }; pub struct ContentVisitor; From 0485503247f11dbda2dbccf298a08ca4873088e4 Mon Sep 17 00:00:00 2001 From: Bilal Mahmoud Date: Wed, 7 Jun 2023 18:38:38 +0200 Subject: [PATCH 11/12] feat: ContentDeserializer (WIP) --- libs/deer/src/content.rs | 58 ++++++-- libs/deer/src/content/deserializer.rs | 162 ++++++++++++++++++--- libs/deer/src/value.rs | 4 +- libs/deer/src/value/array.rs | 146 ++++++++++++++++++- libs/deer/src/value/object.rs | 194 +++++++++++++++++++++++++- 5 files changed, 525 insertions(+), 39 deletions(-) diff --git a/libs/deer/src/content.rs b/libs/deer/src/content.rs index c18c776714d..77713e12bfd 100644 --- a/libs/deer/src/content.rs +++ b/libs/deer/src/content.rs @@ -7,15 +7,13 @@ use alloc::{borrow::ToOwned, string::String, vec::Vec}; use error_stack::{Report, Result, ResultExt}; use crate::{ - content::visitor::ContentVisitor, + content::{deserializer::ContentDeserializer, visitor::ContentVisitor}, error::{ - ArrayAccessError, DeserializeError, DeserializerError, Error, ExpectedType, MissingError, - ObjectAccessError, ReceivedValue, TypeError, ValueError, Variant, VisitorError, + DeserializeError, Error, ExpectedType, MissingError, ReceivedValue, ValueError, Variant, }, - ext::TupleExt, - sealed::T, - ArrayAccess, Context, Deserialize, Deserializer, Document, EnumVisitor, Number, ObjectAccess, - OptionalVisitor, Reflection, Visitor, + schema::visitor::{ArraySchema, ObjectSchema}, + value::IntoDeserializer, + Context, Deserialize, Deserializer, Document, Number, Reflection, Schema, }; mod size_hint { @@ -48,8 +46,36 @@ pub enum Content<'de> { Object(Vec<(Content<'de>, Content<'de>)>), } +impl Content<'_> { + fn schema(&self) -> Option { + match self { + Self::Bool(_) => Some(Document::new::()), + Self::Number(_) => Some(Document::new::()), + Self::I128(_) => Some(Document::new::()), + Self::U128(_) => Some(Document::new::()), + Self::Char(_) => Some(Document::new::()), + Self::String(_) | Self::Str(_) => Some(Document::new::()), + Self::ByteBuf(_) | Self::Bytes(_) => Some(Document::new::<[u8]>()), + Self::Null => Some(Document::new::<<() as Deserialize>::Reflection>()), + Self::None => None, + Self::Array(_) => Some(Document::new::()), + Self::Object(_) => Some(Document::new::()), + } + } +} + +pub struct ContentReflection; + +impl Reflection for ContentReflection { + fn schema(_: &mut Document) -> Schema { + // TODO: we cannot properly reflect the schema of `Content`, as it accepts any value, and we + // cannot express ors (or the top type) in any way with the current schema system + Schema::new("any") + } +} + impl<'de> Deserialize<'de> for Content<'de> { - type Reflection = (); + type Reflection = ContentReflection; fn deserialize>(de: D) -> Result { de.deserialize_any(ContentVisitor) @@ -57,15 +83,25 @@ impl<'de> Deserialize<'de> for Content<'de> { } } +impl<'de> IntoDeserializer<'de> for Content<'de> { + type Deserializer<'a> = ContentDeserializer<'a, 'de> where Self: 'a; + + fn into_deserializer<'a>(self, context: &'a Context) -> Self::Deserializer<'a> + where + Self: 'a, + { + ContentDeserializer::new(self, context) + } +} + // TODO: OptionalVisitor // TODO: EnumVisitor // TODO: FieldVisitor (?) -// TODO: Reflection c: // TODO: to shortcircuit `Content` (if deserialized multiple times) serde actually uses a // `__deserialize_content` -fn invalid_type(received: Content) -> Report { +fn invalid_type(received: Content, expecting: Document) -> Report { let received = match received { Content::Bool(value) => Some(ReceivedValue::new(value)), Content::Number(value) => Some(ReceivedValue::new(value)), @@ -83,7 +119,7 @@ fn invalid_type(received: Content) -> Report { Content::Object(value) => todo!(), }; - let expected = ExpectedType::new(T::document()); + let expected = ExpectedType::new(expecting); match received { None => Report::new(MissingError.into_error()).attach(expected), diff --git a/libs/deer/src/content/deserializer.rs b/libs/deer/src/content/deserializer.rs index 7d8fa9bbfde..71b28fd85fe 100644 --- a/libs/deer/src/content/deserializer.rs +++ b/libs/deer/src/content/deserializer.rs @@ -1,23 +1,67 @@ +use alloc::vec::Vec; + use error_stack::{Report, Result, ResultExt}; +use num_traits::ToPrimitive; use crate::{ content::{invalid_type, Content}, - error::{DeserializerError, TypeError, Variant}, + error::{ + DeserializerError, ExpectedType, MissingError, ReceivedType, TypeError, Variant, + VisitorError, + }, ext::TupleExt, sealed::T, - Context, Deserialize, Deserializer, EnumVisitor, IdentifierVisitor, Number, OptionalVisitor, - StructVisitor, Visitor, + value::{ArrayIteratorDeserializer, ObjectIteratorDeserializer}, + Context, Deserializer, EnumVisitor, IdentifierVisitor, OptionalVisitor, StructVisitor, Visitor, }; -struct ContentDeserializer<'a, 'de> { +pub struct ContentDeserializer<'a, 'de> { content: Content<'de>, - context: &'a mut Context, + context: &'a Context, +} + +// Reason: `content` and `context` are the correct names. There are no alternatives. +#[allow(clippy::similar_names)] +fn visit_content_array<'de, V>( + content: Vec>, + context: &Context, + visitor: V, +) -> Result +where + V: Visitor<'de>, +{ + let array = content.into_iter(); + + let deserializer = ArrayIteratorDeserializer::new(context, array); + + deserializer + .deserialize_array(visitor) + .change_context(VisitorError) +} + +// Reason: `content` and `context` are the correct names. There are no alternatives. +#[allow(clippy::similar_names)] +fn visit_content_object<'de, V>( + content: Vec<(Content<'de>, Content<'de>)>, + context: &Context, + visitor: V, +) -> Result +where + V: Visitor<'de>, +{ + let object = content.into_iter(); + + let deserializer = ObjectIteratorDeserializer::new(context, object); + + deserializer + .deserialize_object(visitor) + .change_context(VisitorError) } impl<'a, 'de> ContentDeserializer<'a, 'de> { // Reason: `content` and `context` are the correct names. There are no alternatives. #[allow(clippy::similar_names)] - pub fn new(content: Content<'de>, context: &'a mut Context) -> Self { + pub fn new(content: Content<'de>, context: &'a Context) -> Self { Self { content, context } } } @@ -43,8 +87,8 @@ impl<'de> Deserializer<'de> for ContentDeserializer<'_, 'de> { Content::Bytes(value) => visitor.visit_borrowed_bytes(value), Content::Null => visitor.visit_null(), Content::None => visitor.visit_none(), - Content::Array(_) => todo!(), // SeqIterator - Content::Object(_) => todo!(), // SeqIterator + Content::Array(array) => visit_content_array(array, self.context, visitor), + Content::Object(object) => visit_content_object(object, self.context, visitor), } .change_context(DeserializerError) } @@ -55,8 +99,9 @@ impl<'de> Deserializer<'de> for ContentDeserializer<'_, 'de> { { match self.content { Content::Null => visitor.visit_null().change_context(DeserializerError), - received => Err(invalid_type::<<() as Deserialize>::Reflection>(received) - .change_context(DeserializerError)), + received => { + Err(invalid_type(received, visitor.expecting()).change_context(DeserializerError)) + } } } @@ -66,7 +111,9 @@ impl<'de> Deserializer<'de> for ContentDeserializer<'_, 'de> { { match self.content { Content::Bool(value) => visitor.visit_bool(value).change_context(DeserializerError), - received => Err(invalid_type::(received).change_context(DeserializerError)), + received => { + Err(invalid_type(received, visitor.expecting()).change_context(DeserializerError)) + } } } @@ -78,7 +125,9 @@ impl<'de> Deserializer<'de> for ContentDeserializer<'_, 'de> { Content::Number(value) => visitor .visit_number(value) .change_context(DeserializerError), - received => Err(invalid_type::(received).change_context(DeserializerError)), + received => { + Err(invalid_type(received, visitor.expecting()).change_context(DeserializerError)) + } } } @@ -94,7 +143,9 @@ impl<'de> Deserializer<'de> for ContentDeserializer<'_, 'de> { Content::Str(value) => visitor .visit_borrowed_str(value) .change_context(DeserializerError), - received => Err(invalid_type::(received).change_context(DeserializerError)), + received => { + Err(invalid_type(received, visitor.expecting()).change_context(DeserializerError)) + } } } @@ -122,7 +173,9 @@ impl<'de> Deserializer<'de> for ContentDeserializer<'_, 'de> { Content::Bytes(value) => visitor .visit_borrowed_bytes(value) .change_context(DeserializerError), - received => Err(invalid_type::(received).change_context(DeserializerError)), + received => { + Err(invalid_type(received, visitor.expecting()).change_context(DeserializerError)) + } } } @@ -143,8 +196,12 @@ impl<'de> Deserializer<'de> for ContentDeserializer<'_, 'de> { Content::Bytes(value) => visitor .visit_borrowed_bytes(value) .change_context(DeserializerError), - Content::Array(value) => todo!(), - received => Err(invalid_type::(received).change_context(DeserializerError)), + Content::Array(value) => { + visit_content_array(value, self.context, visitor).change_context(DeserializerError) + } + received => { + Err(invalid_type(received, visitor.expecting()).change_context(DeserializerError)) + } } } @@ -159,14 +216,28 @@ impl<'de> Deserializer<'de> for ContentDeserializer<'_, 'de> { where V: Visitor<'de>, { - todo!() + match self.content { + Content::Array(value) => { + visit_content_array(value, self.context, visitor).change_context(DeserializerError) + } + received => { + Err(invalid_type(received, visitor.expecting()).change_context(DeserializerError)) + } + } } fn deserialize_object(self, visitor: V) -> Result where V: Visitor<'de>, { - todo!() + match self.content { + Content::Object(value) => { + visit_content_object(value, self.context, visitor).change_context(DeserializerError) + } + received => { + Err(invalid_type(received, visitor.expecting()).change_context(DeserializerError)) + } + } } fn deserialize_optional(self, visitor: V) -> Result @@ -234,14 +305,65 @@ impl<'de> Deserializer<'de> for ContentDeserializer<'_, 'de> { where V: StructVisitor<'de>, { - todo!() + match self.content { + Content::Array(array) => visitor + .visit_array(ArrayIteratorDeserializer::new( + self.context, + array.into_iter(), + )) + .change_context(DeserializerError), + Content::Object(object) => visitor + .visit_object(ObjectIteratorDeserializer::new( + self.context, + object.into_iter(), + )) + .change_context(DeserializerError), + other => { + let received = other.schema(); + + if let Some(received) = received { + Err(Report::new(TypeError.into_error()) + .attach(ExpectedType::new(visitor.expecting())) + .attach(ReceivedType::new(received)) + .change_context(DeserializerError)) + } else { + Err(Report::new(MissingError.into_error()) + .attach(ExpectedType::new(visitor.expecting())) + .change_context(DeserializerError)) + } + } + } } fn deserialize_identifier(self, visitor: V) -> Result where V: IdentifierVisitor<'de>, { - todo!() + match self.content { + Content::String(value) => visitor + .visit_str(value.as_str()) + .change_context(DeserializerError), + Content::Str(value) => visitor.visit_str(value).change_context(DeserializerError), + Content::ByteBuf(value) => visitor + .visit_bytes(value.as_slice()) + .change_context(DeserializerError), + Content::Bytes(value) => visitor.visit_bytes(value).change_context(DeserializerError), + Content::Number(value) => { + if let Some(value) = value.to_u8() { + return visitor.visit_u8(value).change_context(DeserializerError); + } + + if let Some(value) = value.to_u64() { + return visitor.visit_u64(value).change_context(DeserializerError); + } + + Err(invalid_type(Content::Number(value), visitor.expecting()) + .change_context(DeserializerError)) + } + other => { + Err(invalid_type(other, visitor.expecting()).change_context(DeserializerError)) + } + } } fn __deserialize_content(self, _: T, visitor: V) -> Result, DeserializerError> diff --git a/libs/deer/src/value.rs b/libs/deer/src/value.rs index 175f51a1c33..6b9f5fb56d4 100644 --- a/libs/deer/src/value.rs +++ b/libs/deer/src/value.rs @@ -708,9 +708,9 @@ mod bytes; mod object; mod string; -pub use array::ArrayAccessDeserializer; +pub use array::{ArrayAccessDeserializer, ArrayIteratorDeserializer}; pub use bytes::{BorrowedBytesDeserializer, BytesBufferDeserializer, BytesDeserializer}; -pub use object::ObjectAccessDeserializer; +pub use object::{ObjectAccessDeserializer, ObjectIteratorDeserializer}; pub use string::{BorrowedStrDeserializer, StrDeserializer, StringDeserializer}; use crate::error::MissingError; diff --git a/libs/deer/src/value/array.rs b/libs/deer/src/value/array.rs index 4cf75eafe8a..d26400cf3e7 100644 --- a/libs/deer/src/value/array.rs +++ b/libs/deer/src/value/array.rs @@ -1,11 +1,16 @@ +use core::iter::Fuse; + use error_stack::{Report, Result, ResultExt}; use crate::{ - error::{DeserializerError, ExpectedType, ReceivedType, TypeError, Variant}, + error::{ + ArrayAccessError, ArrayLengthError, DeserializerError, ExpectedType, ReceivedLength, + ReceivedType, TypeError, Variant, + }, schema::visitor::ArraySchema, - value::EnumUnitDeserializer, - ArrayAccess, Context, Deserializer, EnumVisitor, IdentifierVisitor, OptionalVisitor, - Reflection, StructVisitor, Visitor, + value::{EnumUnitDeserializer, IntoDeserializer}, + ArrayAccess, Context, Deserialize, Deserializer, EnumVisitor, IdentifierVisitor, + OptionalVisitor, Reflection, StructVisitor, Visitor, }; // TODO: SliceDeserializer/IteratorDeserializer @@ -85,3 +90,136 @@ where .change_context(DeserializerError)) } } + +pub struct ArrayIteratorDeserializer<'a, I> { + context: &'a Context, + + dirty: bool, + expected: usize, + + iter: Fuse, +} + +impl<'a, I> ArrayIteratorDeserializer<'a, I> { + #[must_use] + pub const fn new(context: &'a Context, iter: I) -> Self + where + I: Iterator, + { + Self { + context, + + dirty: false, + expected: 0, + + iter: iter.fuse(), + } + } +} + +// TODO: test +impl<'de, I, T> Deserializer<'de> for ArrayIteratorDeserializer<'_, I> +where + I: Iterator, + T: IntoDeserializer<'de>, +{ + forward_to_deserialize_any!( + null + bool + number + i8 i16 i32 i64 i128 + u8 u16 u32 u64 u128 + 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).change_context(DeserializerError) + } + + fn deserialize_optional(self, visitor: V) -> Result + where + V: OptionalVisitor<'de>, + { + visitor.visit_some(self).change_context(DeserializerError) + } + + fn deserialize_enum(self, visitor: V) -> Result + where + V: EnumVisitor<'de>, + { + EnumUnitDeserializer::new(self.context, self).deserialize_enum(visitor) + } + + fn deserialize_struct(self, visitor: V) -> Result + where + V: StructVisitor<'de>, + { + visitor.visit_array(self).change_context(DeserializerError) + } + + fn deserialize_identifier(self, visitor: V) -> Result + where + V: IdentifierVisitor<'de>, + { + Err(Report::new(TypeError.into_error()) + .attach(ExpectedType::new(visitor.expecting())) + .attach(ReceivedType::new(ArraySchema::document())) + .change_context(DeserializerError)) + } +} + +impl<'de, I, T> ArrayAccess<'de> for ArrayIteratorDeserializer<'_, I> +where + I: Iterator, + T: IntoDeserializer<'de>, +{ + fn is_dirty(&self) -> bool { + self.dirty + } + + fn context(&self) -> &Context { + self.context + } + + fn next(&mut self) -> Option> + where + U: Deserialize<'de>, + { + self.dirty = true; + + let deserializer = self.iter.next()?; + let deserializer = deserializer.into_deserializer(self.context); + + let value = U::deserialize(deserializer); + self.expected += 1; + + Some(value.change_context(ArrayAccessError)) + } + + fn size_hint(&self) -> Option { + self.iter.size_hint().1 + } + + fn end(self) -> Result<(), ArrayAccessError> { + let remaining = self.iter.count(); + + if remaining == 0 { + return Ok(()); + } + + let error = ArrayLengthError::new(&self, self.expected) + .attach(ReceivedLength::new(self.expected + remaining)); + + Err(error.change_context(ArrayAccessError)) + } +} diff --git a/libs/deer/src/value/object.rs b/libs/deer/src/value/object.rs index 30d46ee0c2b..5400302bd6e 100644 --- a/libs/deer/src/value/object.rs +++ b/libs/deer/src/value/object.rs @@ -1,12 +1,15 @@ +use core::iter::Fuse; + use error_stack::{Report, Result, ResultExt}; use crate::{ error::{ - DeserializerError, ExpectedLength, ExpectedType, ObjectLengthError, ReceivedLength, - ReceivedType, TypeError, Variant, VisitorError, + DeserializerError, ExpectedLength, ExpectedType, ObjectAccessError, ObjectLengthError, + ReceivedLength, ReceivedType, TypeError, Variant, VisitorError, }, ext::TupleExt, schema::visitor::ObjectSchema, + value::IntoDeserializer, Context, Deserializer, EnumVisitor, FieldVisitor, IdentifierVisitor, ObjectAccess, OptionalVisitor, Reflection, StructVisitor, Visitor, }; @@ -133,3 +136,190 @@ where .change_context(DeserializerError)) } } + +pub struct ObjectIteratorDeserializer<'a, I> { + context: &'a Context, + + dirty: bool, + expected: usize, + + iter: Fuse, +} + +impl<'a, I> ObjectIteratorDeserializer<'a, I> { + #[must_use] + pub const fn new(context: &'a Context, iter: I) -> Self + where + I: Iterator, + { + Self { + context, + + dirty: false, + expected: 0, + + iter: iter.fuse(), + } + } +} + +// TODO: test +impl<'de, I, T, U> Deserializer<'de> for ObjectIteratorDeserializer<'_, I> +where + I: Iterator, + T: IntoDeserializer<'de>, + U: IntoDeserializer<'de>, +{ + forward_to_deserialize_any!( + null + bool + number + i8 i16 i32 i64 i128 + u8 u16 u32 u64 u128 + 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).change_context(DeserializerError) + } + + fn deserialize_optional(self, visitor: V) -> Result + where + V: OptionalVisitor<'de>, + { + visitor.visit_some(self).change_context(DeserializerError) + } + + fn deserialize_enum(self, visitor: V) -> Result + where + V: EnumVisitor<'de>, + { + struct EnumFieldVisitor(T); + + impl<'de, T> FieldVisitor<'de> for EnumFieldVisitor + where + T: EnumVisitor<'de>, + { + type Key = T::Discriminant; + type Value = T::Value; + + fn visit_key(&self, deserializer: D) -> Result + where + D: Deserializer<'de>, + { + self.0 + .visit_discriminant(deserializer) + .change_context(VisitorError) + } + + fn visit_value( + self, + key: Self::Key, + deserializer: D, + ) -> Result + where + D: Deserializer<'de>, + { + self.0 + .visit_value(key, deserializer) + .change_context(VisitorError) + } + } + + let mut access = self.into_bound(1).change_context(DeserializerError)?; + + let Some(value) = access.field(EnumFieldVisitor(visitor)) else { + return Err(Report::new(ObjectLengthError.into_error()) + .attach(ExpectedLength::new(1)) + .attach(ReceivedLength::new(0)) + .change_context(DeserializerError)) + }; + + let (value, _) = (value, access.end()) + .fold_reports() + .change_context(DeserializerError)?; + + Ok(value) + } + + fn deserialize_struct(self, visitor: V) -> Result + where + V: StructVisitor<'de>, + { + visitor.visit_object(self).change_context(DeserializerError) + } + + fn deserialize_identifier(self, visitor: V) -> Result + where + V: IdentifierVisitor<'de>, + { + Err(Report::new(TypeError.into_error()) + .attach(ExpectedType::new(visitor.expecting())) + .attach(ReceivedType::new(ObjectSchema::document())) + .change_context(DeserializerError)) + } +} + +impl<'de, I, T, U> ObjectAccess<'de> for ObjectIteratorDeserializer<'_, I> +where + I: Iterator, + T: IntoDeserializer<'de>, + U: IntoDeserializer<'de>, +{ + fn is_dirty(&self) -> bool { + self.dirty + } + + fn context(&self) -> &Context { + self.context + } + + fn try_field( + &mut self, + visitor: F, + ) -> core::result::Result, F> + where + F: FieldVisitor<'de>, + { + self.dirty = true; + + let Some((key, value)) = self.iter.next() else { + return Err(visitor); + }; + + let key = key.into_deserializer(self.context); + let value = value.into_deserializer(self.context); + + let key = visitor.visit_key(key); + let value = key.and_then(|key| visitor.visit_value(key, value)); + + Ok(value.change_context(ObjectAccessError)) + } + + fn size_hint(&self) -> Option { + self.iter.size_hint().1 + } + + fn end(self) -> Result<(), ObjectAccessError> { + let remaining = self.iter.count(); + + if remaining == 0 { + return Ok(()); + } + + let error = ObjectLengthError::new(&self, self.expected) + .attach(ReceivedLength::new(self.expected + remaining)); + + Err(error.change_context(ObjectAccessError)) + } +} From d3a81a694dcec5b24b8c5240a404a0183afd3624 Mon Sep 17 00:00:00 2001 From: Bilal Mahmoud Date: Wed, 7 Jun 2023 18:46:37 +0200 Subject: [PATCH 12/12] chore: readability improvement for visitor --- libs/deer/src/content/visitor.rs | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/libs/deer/src/content/visitor.rs b/libs/deer/src/content/visitor.rs index a4985efacee..d06011a0795 100644 --- a/libs/deer/src/content/visitor.rs +++ b/libs/deer/src/content/visitor.rs @@ -4,8 +4,8 @@ use error_stack::Result; use crate::{ content::{size_hint, Content}, - error::{ArrayAccessError, ObjectAccessError, VisitorError}, - ArrayAccess, Document, Number, ObjectAccess, Visitor, + error::{ArrayAccessError, ObjectAccessError, ResultExtPrivate, VisitorError}, + ArrayAccess, Deserialize, Document, Number, ObjectAccess, Visitor, }; pub struct ContentVisitor; @@ -14,7 +14,7 @@ impl<'de> Visitor<'de> for ContentVisitor { type Value = Content<'de>; fn expecting(&self) -> Document { - todo!() + Self::Value::reflection() } fn visit_none(self) -> Result { @@ -69,14 +69,11 @@ impl<'de> Visitor<'de> for ContentVisitor { let mut errors: Result<(), ArrayAccessError> = Ok(()); while let Some(entry) = v.next() { - match (entry, &mut errors) { - (Ok(entry), Ok(_)) => array.push(entry), + match entry { + Ok(entry) if errors.is_ok() => array.push(entry), // we don't need to allocate if we already have an error - (Ok(_), Err(_)) => {} - (Err(error), Err(errors)) => { - errors.extend_one(error); - } - (Err(error), errors) => *errors = Err(error), + Ok(_) => {} + Err(error) => errors.extend_one(error), } } @@ -91,14 +88,11 @@ impl<'de> Visitor<'de> for ContentVisitor { let mut errors: Result<(), ObjectAccessError> = Ok(()); while let Some(entry) = v.next() { - match (entry, &mut errors) { - (Ok(entry), Ok(_)) => object.push(entry), + match entry { + Ok(entry) if errors.is_ok() => object.push(entry), // we don't need to allocate if we already have an error - (Ok(_), Err(_)) => {} - (Err(error), Err(errors)) => { - errors.extend_one(error); - } - (Err(error), errors) => *errors = Err(error), + Ok(_) => {} + Err(error) => errors.extend_one(error), } }