Diffstat (limited to 'src/walk/walkers/serde/deserializer.rs')
| -rw-r--r-- | src/walk/walkers/serde/deserializer.rs | 345 |
1 files changed, 333 insertions, 12 deletions
diff --git a/src/walk/walkers/serde/deserializer.rs b/src/walk/walkers/serde/deserializer.rs index cabffab..f5d9628 100644 --- a/src/walk/walkers/serde/deserializer.rs +++ b/src/walk/walkers/serde/deserializer.rs @@ -1,15 +1,336 @@ -use crate::{effect::Effect, Walker}; +use serde::Deserializer; -pub struct DeserializerWalker<T> { - deserializer: T, +use crate::{ + any::{BorrowedStaticHrt, OwnedStatic, TempBorrowedStatic, TempBorrowedStaticHrt, TypeName}, any_trait, effect::{ + BlockOn, Effect, EffectExt as _, Effective as _, EffectiveExt as _, ErasedEffective, Ss, + }, hkt::Marker, protocol::{ + visitor::{request_hint, visit_value, ValueKnown, ValueProto, VisitResult}, + walker::hint::{DynVisitorWith, Hint, HintMeta, HintProto, MetaHint, MetaKnown}, + DynVisitor, DynWalker, + }, walkers::core::int::IntegerWalkerError, Flow, Walker +}; + +pub struct DeserializerWalker<'ctx, T, E> +where + T: Deserializer<'ctx>, +{ + inner: Inner<'ctx, T>, + _marker: Marker<E>, +} + +enum Inner<'ctx, T> +where + T: Deserializer<'ctx>, +{ + Temp, + Init(T), + Error(DeserializerWalkerError<'ctx, T>), + Done, +} + +impl<'ctx, T, E: Effect> DeserializerWalker<'ctx, T, E> +where + T: Deserializer<'ctx>, +{ + pub fn new(deserializer: T) -> Self { + Self { + inner: Inner::Init(deserializer), + _marker: Default::default(), + } + } +} + +pub struct DeserializerWalkerError<'ctx, T> +where + T: Deserializer<'ctx> +{ + inner: VisitorError<'ctx, T>, +} + +impl<'ctx, T> core::fmt::Debug for DeserializerWalkerError<'ctx, T> +where + T: Deserializer<'ctx> +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match &self.inner { + VisitorError::u8(err) => write!(f, "{}", err), + VisitorError::u16(err) => write!(f, "{}", err), + VisitorError::u32(err) => write!(f, "{}", err), + VisitorError::u64(err) => write!(f, "{}", err), + VisitorError::u128(err) => write!(f, "{}", err), + VisitorError::usize(err) => write!(f, "{}", err), + VisitorError::i8(err) => write!(f, "{}", err), + VisitorError::i16(err) => write!(f, "{}", err), + VisitorError::i32(err) => write!(f, "{}", err), + VisitorError::i64(err) => write!(f, "{}", err), + VisitorError::i128(err) => write!(f, "{}", err), + VisitorError::isize(err) => write!(f, "{}", err), + VisitorError::Serde(err) => write!(f, "{}", err), + } + } +} + +impl<'ctx, T> core::fmt::Display for DeserializerWalkerError<'ctx, T> +where + T: Deserializer<'ctx> +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:?}", self) + } +} + +impl<'ctx, T, E: Effect> Walker<'ctx, E> for DeserializerWalker<'ctx, T, E> +where + T: Deserializer<'ctx> + Ss, + T::Error: Ss, +{ + type Error = DeserializerWalkerError<'ctx, T>; + + type Output = (); + + fn walk<'visitor: 'effect, 'effect>( + self, + visitor: DynVisitor<'visitor, 'ctx>, + ) -> ErasedEffective<'effect, Result<Self::Output, Self::Error>, E> + where + Self: 'effect, + { + E::as_ctx((self, visitor), |(this, visitor)| { + // Serde deserializers usually prefer that a hint method is called rather than _any. + // As such we need to ask the visitor for a hint first. + request_hint::<E>(visitor.cast(), DynWalker(this)) + .map(VisitResult::unit_skipped) + .cast() + }) + .map(|((this, _), _)| match this.inner { + Inner::Temp => todo!(), + Inner::Init(_) => todo!(), + Inner::Error(err) => Err(err), + Inner::Done => Ok(()), + }) + } +} + +any_trait! { + impl['ctx, T, E] DeserializerWalker<'ctx, T, E> = [ + HintProto<ValueProto<OwnedStatic<bool>, E>>, + HintProto<ValueProto<OwnedStatic<i8>, E>>, + HintProto<ValueProto<OwnedStatic<i16>, E>>, + HintProto<ValueProto<OwnedStatic<i32>, E>>, + HintProto<ValueProto<OwnedStatic<i64>, E>>, + HintProto<ValueProto<OwnedStatic<i128>, E>>, + HintProto<ValueProto<OwnedStatic<u8>, E>>, + HintProto<ValueProto<OwnedStatic<u16>, E>>, + HintProto<ValueProto<OwnedStatic<u32>, E>>, + HintProto<ValueProto<OwnedStatic<u64>, E>>, + HintProto<ValueProto<OwnedStatic<u128>, E>>, + HintProto<ValueProto<OwnedStatic<char>, E>>, + ] where + T: Deserializer<'ctx> + Ss, + T::Error: Ss, + E: Effect +} + +impl<'ctx, T, E: Effect> DeserializerWalker<'ctx, T, E> +where + T: Deserializer<'ctx>, +{ + fn call_deserialize<'visitor, 'e, F>(&mut self, f: F) -> ErasedEffective<'e, VisitResult, E> + where + F: FnOnce(T) -> Result<(Option<VisitorError<'ctx, T>>, VisitResult), T::Error>, + { + match core::mem::replace(&mut self.inner, Inner::Temp) { + Inner::Init(deserializer) => { + match f(deserializer) { + Ok((None, result)) => { + // Return the flow the visitor decided on. + self.inner = Inner::Done; + E::ready(result) + } + Ok((Some(err), _)) => { + self.inner = Inner::Error(DeserializerWalkerError { inner: err}); + E::ready(Flow::Err.into()) + } + Err(err) => { + // There was an error from serde so record it and signal an error with the + // flow. + self.inner = Inner::Error(DeserializerWalkerError { inner: VisitorError::Serde(err) }); + E::ready(Flow::Err.into()) + } + } + } + inner => { + // We really shouldn't be in this situation... + self.inner = inner; + E::ready(VisitResult::Skipped(())) + } + } + } +} + +macro_rules! impl_hints { + (<$ctx:lifetime, $T:ident, $E:ident> $($proto:ty => $method:ident: $type:ty),* $(,)?) => { + $(impl<$ctx, $T, $E: Effect> Hint<$ctx, $proto> for DeserializerWalker<$ctx, $T, $E> + where + $T: Deserializer<$ctx>, + { + fn hint<'this: 'e, 'visitor: 'e, 'hint: 'e, 'e>( + &'this mut self, + visitor: DynVisitorWith<'visitor, $ctx, $proto>, + _hint: MetaHint<'hint, $ctx, $proto>, + ) -> ErasedEffective<'e, VisitResult, $E> + where + $ctx: 'this + 'visitor + 'hint + 'e, + { + self.call_deserialize(|deserializer| deserializer.$method(Visitor::new(visitor, stringify!($type)))) + } + + fn known<'a>( + &'a mut self, + _hint: &'a MetaHint<'a, 'ctx, $proto>, + ) -> ErasedEffective<'a, Result<MetaKnown<'a, 'ctx, $proto>, ()>, $E> { + E::ready(Ok(ValueKnown { preview: None })) + } + })* + }; +} + +impl_hints! { + <'ctx, T, E> + ValueProto<OwnedStatic<bool>, E> => deserialize_bool: bool, + ValueProto<OwnedStatic<i8>, E> => deserialize_i8: i8, + ValueProto<OwnedStatic<i16>, E> => deserialize_i16: i16, + ValueProto<OwnedStatic<i32>, E> => deserialize_i32: i32, + ValueProto<OwnedStatic<i64>, E> => deserialize_i64: i64, + ValueProto<OwnedStatic<i128>, E> => deserialize_i128: i128, + ValueProto<OwnedStatic<u8>, E> => deserialize_u8: u8, + ValueProto<OwnedStatic<u16>, E> => deserialize_u16: u16, + ValueProto<OwnedStatic<u32>, E> => deserialize_u32: u32, + ValueProto<OwnedStatic<u64>, E> => deserialize_u64: u64, + ValueProto<OwnedStatic<u128>, E> => deserialize_u128: u128, + ValueProto<OwnedStatic<f32>, E> => deserialize_f32: f32, + ValueProto<OwnedStatic<f64>, E> => deserialize_f64: f64, + ValueProto<OwnedStatic<char>, E> => deserialize_char: char, + ValueProto<TempBorrowedStaticHrt<str>, E> => deserialize_str: &str, + ValueProto<BorrowedStaticHrt<str>, E> => deserialize_str: &str, + ValueProto<OwnedStatic<String>, E> => deserialize_string: String, + ValueProto<TempBorrowedStaticHrt<[u8]>, E> => deserialize_bytes: &[u8], + ValueProto<BorrowedStaticHrt<[u8]>, E> => deserialize_bytes: &[u8], + ValueProto<OwnedStatic<Vec<u8>>, E> => deserialize_byte_buf: Vec<u8>, + // ... +} + +#[allow(non_camel_case_types, unused)] +enum VisitorError<'ctx, T> +where + T: Deserializer<'ctx> +{ + u8(IntegerWalkerError<u8>), + u16(IntegerWalkerError<u16>), + u32(IntegerWalkerError<u32>), + u64(IntegerWalkerError<u64>), + u128(IntegerWalkerError<u128>), + usize(IntegerWalkerError<usize>), + i8(IntegerWalkerError<i8>), + i16(IntegerWalkerError<i16>), + i32(IntegerWalkerError<i32>), + i64(IntegerWalkerError<i64>), + i128(IntegerWalkerError<i128>), + isize(IntegerWalkerError<isize>), + Serde(T::Error), +} + +struct Visitor<'temp, 'ctx, T, E> +where + T: Deserializer<'ctx> +{ + wanted: &'static str, + visitor: DynVisitor<'temp, 'ctx>, + error: Option<VisitorError<'ctx, T>>, + _marker: Marker<(T, E)>, +} + +impl<'temp, 'ctx, T, E> Visitor<'temp, 'ctx, T, E> +where + T: Deserializer<'ctx> +{ + pub fn new<Protocol: HintMeta<Effect = E>>( + visitor: DynVisitorWith<'temp, 'ctx, Protocol>, + wanted: &'static str, + ) -> Self { + Self { + wanted, + visitor: visitor.into_inner(), + error: None, + _marker: Default::default(), + } + } } -// impl<'ctx, T, E: Effect> Walker<'ctx, E> for DeserializerWalker<T> { -// -// } -// -// impl<T> WalkerTypes for DeserializerWalker<T> { -// type Error; -// -// type Output; -// } +macro_rules! impl_visits { + (@owned $($method:ident: $type:ty),* $(,)?) => { + $(fn $method<Err>(mut self, v: $type) -> Result<Self::Value, Err> + where + Err: serde::de::Error, + { + // Visit the treaty visitor with the value serde gave. + let result = visit_value::<_, E>(self.visitor.cast(), OwnedStatic(v)); + + // Because serde is sync we need to block on the effective here. + // We use the block on impl the effect context says to use. + let value = E::BlockOn::block_on(result.into_future()); + + // If the visitor skipped the value we just report a continue. + Ok((self.error, value.unit_skipped())) + })* + }; + // Many serde deserializers (like serde_json) don't follow the hint given when visiting. + // So we need to use the full integer walker that can visit all the types. + (@int $($method:ident: $type:ident),* $(,)?) => { + $(fn $method<Err>(mut self, v: $type) -> Result<Self::Value, Err> + where + Err: serde::de::Error, + { + let result = $crate::walkers::core::int::IntegerWalker::<_, E>::new(v).walk(self.visitor.cast()); + + // Because serde is sync we need to block on the effective here. + // We use the block on impl the effect context says to use. + if let Err(err) = E::BlockOn::block_on(result.into_future()) { + self.error = Some(VisitorError::$type(err)) + } + + Ok((self.error, VisitResult::Control(Flow::Done))) + })* + }; +} + +impl<'temp, 'ctx, T: Deserializer<'ctx>, E: Effect> serde::de::Visitor<'ctx> for Visitor<'temp, 'ctx, T, E> { + type Value = (Option<VisitorError<'ctx, T>>, VisitResult); + + fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + formatter.write_str(&self.wanted) + } + + impl_visits! { + @owned + visit_bool: bool, + visit_char: char, + visit_string: String, + visit_byte_buf: Vec<u8>, + // ... + } + + impl_visits! { + @int + visit_i8: i8, + visit_i16: i16, + visit_i32: i32, + visit_i64: i64, + visit_i128: i128, + visit_u8: u8, + visit_u16: u16, + visit_u32: u32, + visit_u64: u64, + visit_u128: u128, + } +} |