33 files changed, 1013 insertions, 304 deletions
@@ -13,7 +13,7 @@ readme = "README.md" macro_rules_attribute = "0.2.0" futures = "0.3.30" pin-project = "1.1.5" -serde = { version = "1.0", default-features = false, optional = true } +serde = { version = "1.0", optional = true } [features] default = ["std", "serde"] diff --git a/src/build/builders/core.rs b/src/build/builders/core.rs index 4a7182d..55c7bfb 100644 --- a/src/build/builders/core.rs +++ b/src/build/builders/core.rs @@ -32,7 +32,7 @@ any_trait! { impl NoopVisitor { pub fn walk_dyn<'ctx: 'e, 'walker: 'e, 'e, E: Effect>( walker: DynWalkerObjSafe<'walker, 'ctx, E>, - ) -> ErasedEffective<'e, VisitResult<DynWalkerObjSafe<'walker, 'ctx, E>>, E> { + ) -> ErasedEffective<'e, VisitResult, E> { E::ready(NoopVisitor::new()).as_ctx_map(|noop| { walker .walk(DynVisitor(noop)) diff --git a/src/build/builders/core/enum.rs b/src/build/builders/core/enum.rs index 92e3149..4e29bc4 100644 --- a/src/build/builders/core/enum.rs +++ b/src/build/builders/core/enum.rs @@ -147,7 +147,7 @@ where fn visit<'a>( &'a mut self, scope: DynRecoverableScope<'a, 'ctx, E>, - ) -> ErasedEffective<'a, VisitResult<DynRecoverableScope<'a, 'ctx, E>>, E> { + ) -> ErasedEffective<'a, VisitResult, E> { match core::mem::replace(&mut self.inner, Inner::Temp) { Inner::Seed(seed) => Info::guess_variant(seed, scope).map(|result| { self.inner = Inner::Value(result); @@ -186,7 +186,7 @@ where &'a mut self, _kind: tags::Variant, walker: DynWalkerObjSafe<'b, 'ctx, E>, - ) -> ErasedEffective<'c, VisitResult<DynWalkerObjSafe<'b, 'ctx, E>>, E> { + ) -> ErasedEffective<'c, VisitResult, E> { let visitor = VariantVisitor::<Info, Mode, E> { marker: None }; E::as_ctx((visitor, walker), |(visitor, walker)| { diff --git a/src/build/builders/core/struct.rs b/src/build/builders/core/struct.rs index 890ffda..6bc1ef1 100644 --- a/src/build/builders/core/struct.rs +++ b/src/build/builders/core/struct.rs @@ -243,19 +243,22 @@ where E: Effect, { #[inline(always)] - fn request_hint<'a>( - &'a mut self, - walker: DynWalker<'a, 'ctx>, - ) -> ErasedEffective<'a, VisitResult<DynWalker<'a, 'ctx>>, E> { + fn request_hint<'this: 'e, 'walker: 'e, 'e>( + &'this mut self, + walker: DynWalker<'walker, 'ctx>, + ) -> ErasedEffective<'e, VisitResult, E> + where + 'ctx: 'this + 'walker, + { E::as_ctx((self, walker), |(this, walker)| { // Start with a hint to use the value protocol to directly transfer the // struct value. - hint_protocol::<ValueProto<Info::ValueT, E>>(walker.cast(), *this, ()).cast() + hint_protocol::<ValueProto<Info::ValueT, E>, _>(walker.cast(), *this, ()).cast() }) .if_not_finished(|(this, walker)| { // Next hint that the struct protocol should be used to switch into // map-like if the walker supports it. - hint_protocol::<TagProto<tags::Struct, E>>( + hint_protocol::<TagProto<tags::Struct, E>, _>( walker.cast(), *this, TagHint { kind: TagConst }, @@ -266,7 +269,7 @@ where // If the struct hint didn't work, // then hint that the map protocol should be used to switch into // map-like if the walker supports it. - hint_protocol::<TagProto<tags::Map, E>>( + hint_protocol::<TagProto<tags::Map, E>, _>( walker.cast(), *this, TagHint { kind: TagConst }, @@ -276,7 +279,7 @@ where .if_not_finished(|(this, walker)| { // Lastly hint to use a sequence to get the field values. // We hint with the exact number of fields we are expecting. - hint_protocol::<SequenceProto<E>>( + hint_protocol::<SequenceProto<E>, _>( walker.cast(), *this, SequenceHint { @@ -285,10 +288,7 @@ where ) .cast() }) - .map(|((_, walker), result)| match result { - VisitResult::Skipped(()) => VisitResult::Skipped(walker), - VisitResult::Control(flow) => VisitResult::Control(flow), - }) + .remove_ctx() } } @@ -328,7 +328,7 @@ where &'this mut self, _kind: tags::Struct, walker: DynWalkerObjSafe<'walker, 'ctx, E>, - ) -> ErasedEffective<'e, VisitResult<DynWalkerObjSafe<'walker, 'ctx, E>>, E> { + ) -> ErasedEffective<'e, VisitResult, E> { // If this protocol is used then we need to create the builders. E::as_ctx(self, |this| this.make_builders().cast()).then(|(this, _)| { if let Inner::Builders { kind, .. } = &mut this.inner { @@ -354,7 +354,7 @@ where &'this mut self, _kind: tags::Map, walker: DynWalkerObjSafe<'walker, 'ctx, E>, - ) -> ErasedEffective<'e, VisitResult<DynWalkerObjSafe<'walker, 'ctx, E>>, E> { + ) -> ErasedEffective<'e, VisitResult, E> { // If this protocol is used then we need to create the builders. E::as_ctx(self, |this| this.make_builders().cast()).then(|(this, _)| { if let Inner::Builders { kind, .. } = &mut this.inner { @@ -382,7 +382,7 @@ where fn visit<'a: 'c, 'b: 'c, 'c>( &'a mut self, scope: DynSequenceScope<'b, 'ctx, E>, - ) -> ErasedEffective<'c, VisitResult<DynSequenceScope<'b, 'ctx, E>>, E> + ) -> ErasedEffective<'c, VisitResult, E> where 'ctx: 'a + 'b + 'c, { @@ -481,7 +481,7 @@ where &'a mut self, _key: tags::Key, walker: DynWalkerObjSafe<'b, 'ctx, E>, - ) -> ErasedEffective<'c, VisitResult<DynWalkerObjSafe<'b, 'ctx, E>>, E> { + ) -> ErasedEffective<'c, VisitResult, E> { let visitor = NameVisitor::<I, M, E> { field_marker: None, _marker: Default::default(), diff --git a/src/build/builders/core/value.rs b/src/build/builders/core/value.rs index b969ac2..22fe995 100644 --- a/src/build/builders/core/value.rs +++ b/src/build/builders/core/value.rs @@ -106,14 +106,17 @@ any_trait! { } impl<'ctx, T: Ss + 'static, E: Effect> RequestHint<'ctx, E> for ValueBuilder<T, NotCloneable, E> { - fn request_hint<'a>( - &'a mut self, - walker: DynWalker<'a, 'ctx>, - ) -> ErasedEffective<'a, VisitResult<DynWalker<'a, 'ctx>>, E> { + fn request_hint<'this: 'e, 'walker: 'e, 'e>( + &'this mut self, + walker: DynWalker<'walker, 'ctx>, + ) -> ErasedEffective<'e, VisitResult, E> + where + 'ctx: 'this + 'walker, + { E::as_ctx((self, walker), |(this, walker)| { - hint_protocol::<ValueProto<OwnedStatic<T>, E>>(walker.cast(), *this, ()).cast() + hint_protocol::<ValueProto<OwnedStatic<T>, E>, _>(walker.cast(), *this, ()).cast() }) - .map(|((_, walker), result)| result.map_skipped(|_| walker)) + .remove_ctx() } } @@ -121,28 +124,32 @@ impl<'ctx, T: Ss + 'static, E: Effect> RequestHint<'ctx, E> for ValueBuilder<T, where T: Clone, { - fn request_hint<'a>( - &'a mut self, - walker: DynWalker<'a, 'ctx>, - ) -> ErasedEffective<'a, VisitResult<DynWalker<'a, 'ctx>>, E> { + fn request_hint<'this: 'e, 'walker: 'e, 'e>( + &'this mut self, + walker: DynWalker<'walker, 'ctx>, + ) -> ErasedEffective<'e, VisitResult, E> + where + 'ctx: 'this + 'walker, + { E::as_ctx((self, walker), |(this, walker)| { - hint_protocol::<ValueProto<OwnedStatic<T>, E>>(walker.cast(), *this, ()).cast() + hint_protocol::<ValueProto<OwnedStatic<T>, E>, _>(walker.cast(), *this, ()).cast() }) .if_not_finished(|(this, walker)| { - hint_protocol::<ValueProto<BorrowedStaticHrt<T>, E>>(walker.cast(), *this, ()).cast() + hint_protocol::<ValueProto<BorrowedStaticHrt<T>, E>, _>(walker.cast(), *this, ()).cast() }) .if_not_finished(|(this, walker)| { - hint_protocol::<ValueProto<TempBorrowedStaticHrt<T>, E>>(walker.cast(), *this, ()) + hint_protocol::<ValueProto<TempBorrowedStaticHrt<T>, E>, _>(walker.cast(), *this, ()) .cast() }) .if_not_finished(|(this, walker)| { - hint_protocol::<ValueProto<BorrowedMutStaticHrt<T>, E>>(walker.cast(), *this, ()).cast() + hint_protocol::<ValueProto<BorrowedMutStaticHrt<T>, E>, _>(walker.cast(), *this, ()) + .cast() }) .if_not_finished(|(this, walker)| { - hint_protocol::<ValueProto<TempBorrowedMutStaticHrt<T>, E>>(walker.cast(), *this, ()) + hint_protocol::<ValueProto<TempBorrowedMutStaticHrt<T>, E>, _>(walker.cast(), *this, ()) .cast() }) - .map(|((_, walker), result)| result.map_skipped(|_| walker)) + .remove_ctx() } } diff --git a/src/effect.rs b/src/effect.rs index 092705e..0b1a76b 100644 --- a/src/effect.rs +++ b/src/effect.rs @@ -95,7 +95,16 @@ pub type ErasedEffective<'lt, Output, E, Bound = ()> = < Erased::ForLt<'lt, Output, E, &'lt (Output, Bound)> >::Effective; +pub trait BlockOn: Ss + Sized + 'static { + fn block_on<F>(future: F) -> F::Output + where + F: core::future::Future + Ss, + <F as core::future::Future>::Output: Ss; +} + pub trait Effect: Join<Effect = Self> + TryJoin<Effect = Self> + Ss + Sized + 'static { + type BlockOn: BlockOn; + type Erased<T: Ss>: Erased::Hkt<T, Self>; fn ready<'a, T: Ss + 'a>(value: T) -> ErasedEffective<'a, T, Self>; diff --git a/src/effect/async.rs b/src/effect/async.rs index 1a910d4..c1a6f06 100644 --- a/src/effect/async.rs +++ b/src/effect/async.rs @@ -5,11 +5,13 @@ use pin_project::pin_project; use crate::hkt::Marker; +use self::blocking::Spin; + use super::*; -pub enum Async {} +pub struct Async<B = Spin>(Marker<B>); -pub struct ErasedFutureHrt<T>(Marker<T>); +pub struct ErasedFutureHrt<T, B>(Marker<(T, B)>); enum ErasedFutureKind<'lt, T> { Boxed(Pin<Box<dyn Future<Output = T> + Send + Sync + 'lt>>), @@ -17,8 +19,9 @@ enum ErasedFutureKind<'lt, T> { } #[must_use] -pub struct ErasedFuture<'lt, T> { +pub struct ErasedFuture<'lt, T, B> { kind: ErasedFutureKind<'lt, T>, + _marker: Marker<B>, } #[pin_project(project = EffectFutureKindProj)] @@ -49,20 +52,25 @@ impl<'lt, T> Future for EffectFuture<'lt, T> { } } -impl<'lt, T: Ss, O> Erased::ForLt<'lt, T, Async, &'lt (T, O)> for ErasedFutureHrt<T> { - type Effective = ErasedFuture<'lt, T>; +impl<'lt, T: Ss, B: BlockOn, O> Erased::ForLt<'lt, T, Async<B>, &'lt (T, O)> + for ErasedFutureHrt<T, B> +{ + type Effective = ErasedFuture<'lt, T, B>; } -impl<T: Ss> Erased::Hkt<T, Async> for ErasedFutureHrt<T> { +impl<T: Ss, B: BlockOn> Erased::Hkt<T, Async<B>> for ErasedFutureHrt<T, B> { type Hrt<O> = Self; } -impl Effect for Async { - type Erased<T: Ss> = ErasedFutureHrt<T>; +impl<B: BlockOn> Effect for Async<B> { + type BlockOn = B; + + type Erased<T: Ss> = ErasedFutureHrt<T, B>; fn ready<'a, T: Ss + 'a>(value: T) -> ErasedEffective<'a, T, Self> { ErasedFuture { kind: ErasedFutureKind::Ready(value), + _marker: Default::default(), } } @@ -73,11 +81,12 @@ impl Effect for Async { { ErasedFuture { kind: ErasedFutureKind::Boxed(Box::pin(future)), + _marker: Default::default(), } } } -impl<'lt, U: Ss + 'lt> Effective<'lt> for ErasedFuture<'lt, U> { +impl<'lt, U: Ss + 'lt, B: BlockOn> Effective<'lt> for ErasedFuture<'lt, U, B> { fn cast<'wrap, X>(self) -> ErasedEffective<'wrap, Self::Output, Self::Effect, X> where 'lt: 'wrap, @@ -85,7 +94,7 @@ impl<'lt, U: Ss + 'lt> Effective<'lt> for ErasedFuture<'lt, U> { self } - type Effect = Async; + type Effect = Async<B>; type Output = U; @@ -183,17 +192,19 @@ impl<'lt, U: Ss + 'lt> Effective<'lt> for ErasedFuture<'lt, U> { ErasedFuture { kind: ErasedFutureKind::Boxed(Box::pin(fut)), + _marker: Default::default(), } } fn ready(value: Self::Output) -> Self { ErasedFuture { kind: ErasedFutureKind::Ready(value), + _marker: Default::default(), } } } -impl Join for Async { +impl<B: BlockOn> Join for Async<B> { type Effect = Self; fn two<'a, T0, T1>( @@ -229,7 +240,7 @@ impl Join for Async { } } -impl TryJoin for Async { +impl<B: BlockOn> TryJoin for Async<B> { type Effect = Self; fn two<'a, T0, T1, F0, F1>( diff --git a/src/effect/blocking.rs b/src/effect/blocking.rs index 39ea3ac..75d6063 100644 --- a/src/effect/blocking.rs +++ b/src/effect/blocking.rs @@ -6,13 +6,6 @@ use crate::hkt::Marker; use super::*; -pub trait BlockOn: 'static { - fn block_on<F>(future: F) -> F::Output - where - F: core::future::Future + Send, - <F as core::future::Future>::Output: Send; -} - pub struct Blocking<B = Spin>(Marker<B>); #[must_use] @@ -34,6 +27,8 @@ impl<T: Ss, B: BlockOn> Erased::Hkt<T, Blocking<B>> for Value<T, B> { } impl<B: BlockOn> Effect for Blocking<B> { + type BlockOn = B; + type Erased<T: Ss> = Value<T, B>; fn ready<'a, T: Ss + 'a>(value: T) -> ErasedEffective<'a, T, Self> { @@ -275,7 +275,7 @@ pub mod demo { #[test] fn demo() { - use crate::effect::blocking::BlockOn as _; + use crate::effect::BlockOn as _; assert_eq!(ident(X { a: true, b: false }), Y { a: true, b: false }); diff --git a/src/protocol/visitor.rs b/src/protocol/visitor.rs index bf2b8d2..e414b4c 100644 --- a/src/protocol/visitor.rs +++ b/src/protocol/visitor.rs @@ -20,7 +20,7 @@ pub use value::*; #[derive(Copy, Clone)] #[must_use] -pub enum VisitResult<S> { +pub enum VisitResult<S = ()> { /// The protocol was not used. /// /// This either means the visitor doesn't support the protocol at all, or diff --git a/src/protocol/visitor/recoverable.rs b/src/protocol/visitor/recoverable.rs index ce7a181..b43344f 100644 --- a/src/protocol/visitor/recoverable.rs +++ b/src/protocol/visitor/recoverable.rs @@ -3,7 +3,7 @@ use crate::{ effect::{Effect, ErasedEffective, ReadyExt as _}, hkt::Marker, protocol::{ - walker::hint::{HintMeta, Meta}, + walker::hint::{HasProtocol, HintMeta, Meta}, DynVisitor, }, Status, @@ -15,7 +15,7 @@ pub trait Recoverable<'ctx, E: Effect> { fn visit<'a>( &'a mut self, scope: DynRecoverableScope<'a, 'ctx, E>, - ) -> ErasedEffective<'a, VisitResult<DynRecoverableScope<'a, 'ctx, E>>, E>; + ) -> ErasedEffective<'a, VisitResult, E>; } pub struct RecoverableProto<E: Effect>(Marker<E>); @@ -66,12 +66,14 @@ impl<E: Effect> HintMeta for RecoverableProto<E> { pub fn visit_recoverable<'a, 'ctx, E: Effect>( visitor: DynVisitor<'a, 'ctx>, scope: DynRecoverableScope<'a, 'ctx, E>, -) -> ErasedEffective<'a, VisitResult<DynRecoverableScope<'a, 'ctx, E>>, E> { +) -> ErasedEffective<'a, VisitResult, E> { if let Some(object) = visitor.0.upcast_mut::<RecoverableProto<E>>() { // Allow the visitor to give a hint if it wants. object.visit(scope) } else { // If the visitor doesn't support request hint then we continue. - VisitResult::Skipped(scope).ready() + VisitResult::Skipped(()).ready() } } + +impl<'ctx, T, E: Effect> HasProtocol<RecoverableProto<E>> for T where T: Recoverable<'ctx, E> {} diff --git a/src/protocol/visitor/request_hint.rs b/src/protocol/visitor/request_hint.rs index 0c11fcd..a110adb 100644 --- a/src/protocol/visitor/request_hint.rs +++ b/src/protocol/visitor/request_hint.rs @@ -1,6 +1,8 @@ use crate::{ any::TypeName, - effect::{Effect, ErasedEffective, ReadyExt as _}, + effect::{ + Effect, EffectExt as _, Effective as _, EffectiveExt as _, ErasedEffective, ReadyExt as _, + }, hkt::Marker, protocol::{DynVisitor, DynWalker}, }; @@ -13,10 +15,12 @@ pub trait RequestHint<'ctx, E: Effect> { /// /// `walker` is what the visitor (`self`) will call to give a hint using the /// [`Hint`][crate::builtins::walker::Hint] protocol. - fn request_hint<'a>( - &'a mut self, - walker: DynWalker<'a, 'ctx>, - ) -> ErasedEffective<'a, VisitResult<DynWalker<'a, 'ctx>>, E>; + fn request_hint<'this: 'e, 'walker: 'e, 'e>( + &'this mut self, + walker: DynWalker<'walker, 'ctx>, + ) -> ErasedEffective<'e, VisitResult, E> + where + 'ctx: 'this + 'walker; } pub struct RequestHintProto<E: Effect>(Marker<E>); @@ -43,15 +47,21 @@ where /// If [`Flow::Done`] is returned then the visitor doesn't need any more information and the walker /// should stop walking. /// If [`Flow::Break`] is returned then there was an error and the walker should stop walking. -pub fn request_hint<'a, 'ctx, E: Effect>( - visitor: DynVisitor<'a, 'ctx>, - walker: DynWalker<'a, 'ctx>, -) -> ErasedEffective<'a, VisitResult<DynWalker<'a, 'ctx>>, E> { - if let Some(object) = visitor.0.upcast_mut::<RequestHintProto<E>>() { - // Allow the visitor to give a hint if it wants. - object.request_hint(walker) - } else { - // If the visitor doesn't support request hint then we continue. - VisitResult::Skipped(walker).ready() - } +pub fn request_hint<'ctx: 'visitor + 'walker, 'visitor: 'e, 'walker: 'e, 'e, E: Effect>( + visitor: DynVisitor<'visitor, 'ctx>, + walker: DynWalker<'walker, 'ctx>, +) -> ErasedEffective<'e, VisitResult<DynWalker<'walker, 'ctx>>, E> { + E::as_ctx((visitor, walker), |(visitor, walker)| { + if let Some(object) = visitor.0.upcast_mut::<RequestHintProto<E>>() { + // Allow the visitor to give a hint if it wants. + object + .request_hint(walker.cast()) + .map(|x| x.unit_skipped()) + .cast() + } else { + // If the visitor doesn't support request hint then we continue. + E::ready(VisitResult::Skipped(())).cast() + } + }) + .map(|((_, walker), result)| result.map_skipped(|_| walker)) } diff --git a/src/protocol/visitor/sequence.rs b/src/protocol/visitor/sequence.rs index 4138be1..f261eca 100644 --- a/src/protocol/visitor/sequence.rs +++ b/src/protocol/visitor/sequence.rs @@ -3,7 +3,7 @@ use crate::{ effect::{Effect, ErasedEffective, ReadyExt as _}, hkt::Marker, protocol::{ - walker::hint::{HintMeta, Meta}, + walker::hint::{HasProtocol, HintMeta, Meta}, DynVisitor, }, Flow, @@ -19,7 +19,7 @@ pub trait Sequence<'ctx, E: Effect> { fn visit<'a: 'c, 'b: 'c, 'c>( &'a mut self, scope: DynSequenceScope<'b, 'ctx, E>, - ) -> ErasedEffective<'c, VisitResult<DynSequenceScope<'b, 'ctx, E>>, E> + ) -> ErasedEffective<'c, VisitResult, E> where 'ctx: 'a; } @@ -91,12 +91,14 @@ impl<E: Effect> HintMeta for SequenceProto<E> { pub fn visit_sequence<'a, 'ctx, E: Effect>( visitor: DynVisitor<'a, 'ctx>, scope: DynSequenceScope<'a, 'ctx, E>, -) -> ErasedEffective<'a, VisitResult<DynSequenceScope<'a, 'ctx, E>>, E> { +) -> ErasedEffective<'a, VisitResult, E> { if let Some(object) = visitor.0.upcast_mut::<SequenceProto<E>>() { // Allow the visitor to walk the sequence scope. object.visit(scope) } else { // If the visitor doesn't support sequence then we continue. - VisitResult::Skipped(scope).ready() + VisitResult::Skipped(()).ready() } } + +impl<'ctx, T, E: Effect> HasProtocol<SequenceProto<E>> for T where T: Sequence<'ctx, E> {} diff --git a/src/protocol/visitor/tag.rs b/src/protocol/visitor/tag.rs index a1f189b..ffc692a 100644 --- a/src/protocol/visitor/tag.rs +++ b/src/protocol/visitor/tag.rs @@ -5,7 +5,7 @@ use crate::{ effect::{Effect, Effective, EffectiveExt, ErasedEffective, ReadyExt}, hkt::Marker, protocol::{ - walker::hint::{HintMeta, Meta}, + walker::hint::{HasProtocol, HintMeta, Meta}, DynVisitor, }, symbol::Symbol, @@ -54,7 +54,7 @@ pub trait Tag<'ctx, K: TagKind, E: Effect> { &'a mut self, kind: K, walker: DynWalkerObjSafe<'b, 'ctx, E>, - ) -> ErasedEffective<'c, VisitResult<DynWalkerObjSafe<'b, 'ctx, E>>, E>; + ) -> ErasedEffective<'c, VisitResult, E>; } pub struct TagProto<K: TagKind, E: Effect>(Marker<(K, E)>); @@ -221,3 +221,5 @@ fn map_walker_err<'ctx, K: TagKind, W: Walker<'ctx, E>, E: Effect>( DynWalkerError::WasWalked(_) => TagError::was_walked(kind), } } + +impl<'ctx, T, K: TagKind, E: Effect> HasProtocol<TagProto<K, E>> for T where T: Tag<'ctx, K, E> {} diff --git a/src/protocol/visitor/value.rs b/src/protocol/visitor/value.rs index 82c7141..8a42627 100644 --- a/src/protocol/visitor/value.rs +++ b/src/protocol/visitor/value.rs @@ -7,7 +7,7 @@ use crate::{ effect::{Effect, ErasedEffective, ReadyExt as _}, hkt::Marker, protocol::{ - walker::hint::{HintMeta, Meta}, + walker::hint::{HasProtocol, HintMeta, Meta}, DynVisitor, }, }; @@ -111,3 +111,8 @@ pub fn visit_value< VisitResult::Skipped(value).ready() } } + +impl<'ctx, T, U: TypeName::MemberType, E: Effect> HasProtocol<ValueProto<U, E>> for T where + T: Value<'ctx, U, E> +{ +} diff --git a/src/protocol/walker/hint.rs b/src/protocol/walker/hint.rs index 683436a..049d881 100644 --- a/src/protocol/walker/hint.rs +++ b/src/protocol/walker/hint.rs @@ -4,11 +4,13 @@ //! this module gives a protocol by which a visitor can give a hint //! to the walker about what it is expecting. +use core::ops::{Deref, DerefMut}; + use crate::{ - any::TypeName, - effect::{Effect, EffectiveExt as _, ErasedEffective, ReadyExt as _}, + any::{AnyTrait, TypeName}, + effect::{Effect, EffectiveExt as _, ErasedEffective, ReadyExt as _, Ss}, hkt::Marker, - protocol::{visitor::VisitResult, DynWalker}, + protocol::{visitor::VisitResult, DynVisitor, DynWalker}, Flow, }; @@ -72,9 +74,9 @@ pub trait Hint<'ctx, Protocol: ?Sized + HintMeta> { /// This should only be called once per [`RequestHint`]. fn hint<'this: 'e, 'visitor: 'e, 'hint: 'e, 'e>( &'this mut self, - visitor: &'visitor mut TypeName::T<'visitor, 'ctx, Protocol>, + visitor: DynVisitorWith<'visitor, 'ctx, Protocol>, hint: MetaHint<'hint, 'ctx, Protocol>, - ) -> ErasedEffective<'e, Flow, Protocol::Effect> + ) -> ErasedEffective<'e, VisitResult, Protocol::Effect> where 'ctx: 'this + 'visitor + 'hint + 'e; @@ -85,6 +87,47 @@ pub trait Hint<'ctx, Protocol: ?Sized + HintMeta> { ) -> ErasedEffective<'a, Result<MetaKnown<'a, 'ctx, Protocol>, ()>, Protocol::Effect>; } +pub struct DynVisitorWith<'temp, 'ctx, Protocol: ?Sized> { + visitor: DynVisitor<'temp, 'ctx>, + _marker: Marker<Protocol>, +} + +pub trait HasProtocol<Protocol: ?Sized> {} + +impl<'temp, 'ctx: 'temp, Protocol: ?Sized + HintMeta> DynVisitorWith<'temp, 'ctx, Protocol> { + pub fn new<T>(visitor: &'temp mut T) -> Self + where + T: AnyTrait<'ctx> + HasProtocol<Protocol> + Ss, + { + Self { + visitor: DynVisitor(visitor), + _marker: Default::default(), + } + } + + pub fn as_known(&mut self) -> &mut TypeName::T<'_, 'ctx, Protocol> { + self.visitor.upcast_mut::<Protocol>().unwrap() + } + + pub fn into_inner(self) -> DynVisitor<'temp, 'ctx> { + self.visitor + } +} + +impl<'temp, 'ctx, Protocol: ?Sized> Deref for DynVisitorWith<'temp, 'ctx, Protocol> { + type Target = DynVisitor<'temp, 'ctx>; + + fn deref(&self) -> &Self::Target { + &self.visitor + } +} + +impl<'temp, 'ctx, Protocol: ?Sized> DerefMut for DynVisitorWith<'temp, 'ctx, Protocol> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.visitor + } +} + pub struct HintProto<Protocol: ?Sized>(Marker<Protocol>); impl<'a, 'ctx, Protocol: ?Sized> TypeName::MemberTypeForLt<'a, 'ctx, &'a &'ctx ()> @@ -110,13 +153,19 @@ pub fn hint_protocol< 'hint: 'e, 'e, Protocol: ?Sized + HintMeta, + T, >( walker: DynWalker<'walker, 'ctx>, - visitor: &'visitor mut TypeName::T<'visitor, 'ctx, Protocol>, + visitor: &'visitor mut T, hint: MetaHint<'hint, 'ctx, Protocol>, -) -> ErasedEffective<'e, VisitResult<()>, Protocol::Effect> { +) -> ErasedEffective<'e, VisitResult<()>, Protocol::Effect> +where + T: AnyTrait<'ctx> + HasProtocol<Protocol> + Ss, +{ if let Some(object) = walker.0.upcast_mut::<HintProto<Protocol>>() { - object.hint(visitor, hint).map(Into::into) + object + .hint(DynVisitorWith::new(visitor), hint) + .map(Into::into) } else { VisitResult::Skipped(()).ready() } diff --git a/src/transform.rs b/src/transform.rs index b9cc346..fc37e01 100644 --- a/src/transform.rs +++ b/src/transform.rs @@ -42,7 +42,7 @@ pub enum BuildError<B, W> { Both(B, W), } -impl<B, W> core::fmt::Debug for BuildError<B, W> +impl<B, W> core::fmt::Debug for BuildError<B, W> where B: core::fmt::Debug, W: core::fmt::Debug, @@ -57,7 +57,12 @@ where pub trait BuildExt { /// Build a value of this type using the default builder. - fn build<'ctx, W>(walker: W) -> Result<Self, BuildError<<Self::Builder as BuilderTypes>::Error, <W as Walker<'ctx, Blocking>>::Error>> + fn build<'ctx, W>( + walker: W, + ) -> Result< + Self, + BuildError<<Self::Builder as BuilderTypes>::Error, <W as Walker<'ctx, Blocking>>::Error>, + > where Self: Build<'ctx, DefaultMode, Blocking>, <Self::Builder as BuilderTypes>::Seed: Default, @@ -72,7 +77,13 @@ pub trait BuildExt { fn build_async<'ctx, W>( walker: W, - ) -> impl Future<Output = Result<Self, BuildError<<Self::Builder as BuilderTypes>::Error, <W as Walker<'ctx, Async>>::Error>>> + Send + Sync + ) -> impl Future< + Output = Result< + Self, + BuildError<<Self::Builder as BuilderTypes>::Error, <W as Walker<'ctx, Async>>::Error>, + >, + > + Send + + Sync where Self: Build<'ctx, DefaultMode, Async>, <Self::Builder as BuilderTypes>::Seed: Default, diff --git a/src/walk/walkers/core.rs b/src/walk/walkers/core.rs index 6b6b128..bc1fbd6 100644 --- a/src/walk/walkers/core.rs +++ b/src/walk/walkers/core.rs @@ -1,6 +1,7 @@ // pub mod array; pub mod bool; +pub mod int; pub mod key_value; pub mod noop; pub mod r#struct; diff --git a/src/walk/walkers/core/int.rs b/src/walk/walkers/core/int.rs new file mode 100644 index 0000000..97dc54d --- /dev/null +++ b/src/walk/walkers/core/int.rs @@ -0,0 +1,281 @@ +use crate::{ + any::OwnedStatic, + any_trait, + effect::{Effect, EffectExt as _, Effective as _, EffectiveExt as _, ErasedEffective, Ss}, + hkt::Marker, + never::Never, + protocol::{ + visitor::{ + request_hint, visit_value, EffectiveVisitExt as _, ValueKnown, ValueProto, VisitResult, + }, + walker::hint::{DynVisitorWith, Hint, HintProto, MetaHint, MetaKnown}, + DynVisitor, DynWalker, + }, + Flow, Walker, +}; + +pub struct IntegerWalker<T, E> { + value: T, + _marker: Marker<E>, +} + +pub trait Integer: + 'static + + Copy + + core::fmt::Debug + + core::fmt::Display + + Ss + + TryInto<u8> + + TryInto<u16> + + TryInto<u32> + + TryInto<u64> + + TryInto<u128> + + TryInto<usize> + + TryInto<i8> + + TryInto<i16> + + TryInto<i32> + + TryInto<i64> + + TryInto<i128> + + TryInto<isize> +{ +} + +fn try_into<T: TryInto<U>, U>(value: T) -> Option<U> { + value.try_into().ok() +} + +impl<'ctx, T, E> IntegerWalker<T, E> { + pub fn new(value: T) -> Self { + Self { + value, + _marker: Default::default(), + } + } +} + +pub struct IntegerWalkerError<T> { + value: T, +} + +impl<T: Integer> ::core::fmt::Debug for IntegerWalkerError<T> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("IntegerWalkerError") + .field("value", &self.value) + .finish() + } +} + +impl<T: Integer> ::core::fmt::Display for IntegerWalkerError<T> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("IntegerWalkerError") + .field("value", &self.value) + .finish() + } +} + +impl<'ctx, T: Integer, E: Effect> Walker<'ctx, E> for IntegerWalker<T, E> { + type Error = IntegerWalkerError<T>; + + type Output = T; + + fn walk<'visitor: 'effect, 'effect>( + self, + visitor: DynVisitor<'visitor, 'ctx>, + ) -> ErasedEffective<'effect, Result<Self::Output, Self::Error>, E> + where + Self: 'effect, + { + let value = self.value; + + E::as_ctx((self, visitor), move |(this, visitor)| { + request_hint::<E>(visitor.cast(), DynWalker(this)) + .map(VisitResult::unit_skipped) + .cast() + }) + .map(|((_, visitor), result)| (visitor, result)) + .if_skipped(move |visitor| { + if let Some(value) = try_into::<_, i8>(value) { + visit_value::<_, E>(visitor.cast(), OwnedStatic(value)) + .map(VisitResult::unit_skipped) + .cast() + } else { + E::ready(VisitResult::Skipped(())).cast() + } + }) + .if_skipped(move |visitor| { + if let Some(value) = try_into::<_, u8>(value) { + visit_value::<_, E>(visitor.cast(), OwnedStatic(value)) + .map(VisitResult::unit_skipped) + .cast() + } else { + E::ready(VisitResult::Skipped(())).cast() + } + }) + .if_skipped(move |visitor| { + if let Some(value) = try_into::<_, i16>(value) { + visit_value::<_, E>(visitor.cast(), OwnedStatic(value)) + .map(VisitResult::unit_skipped) + .cast() + } else { + E::ready(VisitResult::Skipped(())).cast() + } + }) + .if_skipped(move |visitor| { + if let Some(value) = try_into::<_, u16>(value) { + visit_value::<_, E>(visitor.cast(), OwnedStatic(value)) + .map(VisitResult::unit_skipped) + .cast() + } else { + E::ready(VisitResult::Skipped(())).cast() + } + }) + .if_skipped(move |visitor| { + if let Some(value) = try_into::<_, i32>(value) { + visit_value::<_, E>(visitor.cast(), OwnedStatic(value)) + .map(VisitResult::unit_skipped) + .cast() + } else { + E::ready(VisitResult::Skipped(())).cast() + } + }) + .if_skipped(move |visitor| { + if let Some(value) = try_into::<_, u32>(value) { + visit_value::<_, E>(visitor.cast(), OwnedStatic(value)) + .map(VisitResult::unit_skipped) + .cast() + } else { + E::ready(VisitResult::Skipped(())).cast() + } + }) + .if_skipped(move |visitor| { + if let Some(value) = try_into::<_, i64>(value) { + visit_value::<_, E>(visitor.cast(), OwnedStatic(value)) + .map(VisitResult::unit_skipped) + .cast() + } else { + E::ready(VisitResult::Skipped(())).cast() + } + }) + .if_skipped(move |visitor| { + if let Some(value) = try_into::<_, u64>(value) { + visit_value::<_, E>(visitor.cast(), OwnedStatic(value)) + .map(VisitResult::unit_skipped) + .cast() + } else { + E::ready(VisitResult::Skipped(())).cast() + } + }) + .if_skipped(move |visitor| { + if let Some(value) = try_into::<_, i128>(value) { + visit_value::<_, E>(visitor.cast(), OwnedStatic(value)) + .map(VisitResult::unit_skipped) + .cast() + } else { + E::ready(VisitResult::Skipped(())).cast() + } + }) + .if_skipped(move |visitor| { + if let Some(value) = try_into::<_, u128>(value) { + visit_value::<_, E>(visitor.cast(), OwnedStatic(value)) + .map(VisitResult::unit_skipped) + .cast() + } else { + E::ready(VisitResult::Skipped(())).cast() + } + }) + .if_skipped(move |visitor| { + if let Some(value) = try_into::<_, isize>(value) { + visit_value::<_, E>(visitor.cast(), OwnedStatic(value)) + .map(VisitResult::unit_skipped) + .cast() + } else { + E::ready(VisitResult::Skipped(())).cast() + } + }) + .if_skipped(move |visitor| { + if let Some(value) = try_into::<_, usize>(value) { + visit_value::<_, E>(visitor.cast(), OwnedStatic(value)) + .map(VisitResult::unit_skipped) + .cast() + } else { + E::ready(VisitResult::Skipped(())).cast() + } + }) + .map(move |(_, result)| match result { + VisitResult::Skipped(()) => Err(IntegerWalkerError { value }), + VisitResult::Control(_) => Ok(value), + }) + } +} + +impl<T> Integer for T where + T: 'static + + Copy + + core::fmt::Debug + + core::fmt::Display + + Ss + + TryInto<u8> + + TryInto<u16> + + TryInto<u32> + + TryInto<u64> + + TryInto<u128> + + TryInto<usize> + + TryInto<i8> + + TryInto<i16> + + TryInto<i32> + + TryInto<i64> + + TryInto<i128> + + TryInto<isize> +{ +} + +any_trait! { + impl['ctx, T, E] IntegerWalker<T, 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>>, + ] where + T: Integer, + E: Effect +} + +macro_rules! impl_hints { + ($($type:ty),* $(,)?) => { + $(impl<'ctx, T: Integer, E: Effect> Hint<'ctx, ValueProto<OwnedStatic<$type>, E>> + for IntegerWalker<T, E> + { + fn hint<'this: 'e, 'visitor: 'e, 'hint: 'e, 'e>( + &'this mut self, + visitor: DynVisitorWith<'visitor, 'ctx, ValueProto<OwnedStatic<$type>, E>>, + _hint: MetaHint<'hint, 'ctx, ValueProto<OwnedStatic<$type>, E>>, + ) -> ErasedEffective<'e, crate::protocol::visitor::VisitResult, E> + where + 'ctx: 'this + 'visitor + 'hint + 'e, + { + if let Some(value) = try_into::<_, $type>(self.value) { + visit_value::<_, E>(visitor.into_inner(), OwnedStatic(value)) + .map(VisitResult::unit_skipped) + } else { + E::ready(VisitResult::Skipped(())) + } + } + + fn known<'a>( + &'a mut self, + _hint: &'a MetaHint<'a, 'ctx, ValueProto<OwnedStatic<$type>, E>>, + ) -> ErasedEffective<'a, Result<MetaKnown<'a, 'ctx, ValueProto<OwnedStatic<$type>, E>>, ()>, E> + { + E::ready(Ok(ValueKnown { preview: None })) + } + })* + }; +} + +impl_hints![u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize]; diff --git a/src/walk/walkers/core/struct.rs b/src/walk/walkers/core/struct.rs index e6138fb..f20ab0d 100644 --- a/src/walk/walkers/core/struct.rs +++ b/src/walk/walkers/core/struct.rs @@ -13,7 +13,7 @@ use crate::{ SequenceKnown, SequenceProto, SequenceScope, Tag, TagConst, TagDyn, TagError, TagKnown, TagProto, Value, ValueKnown, ValueProto, VisitResult, }, - walker::hint::{Hint, HintMeta, HintProto, MetaHint, MetaKnown}, + walker::hint::{DynVisitorWith, Hint, HintMeta, HintProto, MetaHint, MetaKnown}, DynVisitor, DynWalker, }, DynWalkerAdapter, Flow, Status, TAG_FIELD_NAMES, TAG_MAP, TAG_STRUCT, TAG_TYPE_ID, @@ -186,9 +186,9 @@ where #[inline(always)] fn hint<'this, 'visitor, 'hint, 'e>( &'this mut self, - _visitor: &'visitor mut (dyn Recoverable<'ctx, E> + Send + Sync), + _visitor: DynVisitorWith<'visitor, 'ctx, RecoverableProto<E>>, _hint: MetaHint<'hint, 'ctx, RecoverableProto<E>>, - ) -> ErasedEffective<'e, Flow, E> + ) -> ErasedEffective<'e, VisitResult, E> where 'ctx: 'this + 'visitor + 'hint + 'e, { @@ -220,9 +220,13 @@ where #[inline(always)] fn hint<'this, 'visitor, 'hint, 'e>( &'this mut self, - _visitor: &'visitor mut (dyn Tag<'ctx, tags::FieldNames, E> + Send + Sync), + _visitor: DynVisitorWith< + 'visitor, + 'ctx, + TagProto<TagConst<{ TAG_FIELD_NAMES.to_int() }>, E>, + >, _hint: MetaHint<'hint, 'ctx, TagProto<TagConst<{ TAG_FIELD_NAMES.to_int() }>, E>>, - ) -> ErasedEffective<'e, Flow, E> + ) -> ErasedEffective<'e, VisitResult, E> where 'ctx: 'this + 'visitor + 'hint + 'e, { @@ -268,9 +272,9 @@ where #[inline(always)] fn hint<'this, 'visitor, 'hint, 'e>( &'this mut self, - _visitor: &'visitor mut (dyn Tag<'ctx, tags::TypeName, E> + Send + Sync), + _visitor: DynVisitorWith<'visitor, 'ctx, TagProto<TagConst<{ TAG_TYPE_NAME.to_int() }>, E>>, _hint: MetaHint<'hint, 'ctx, TagProto<TagConst<{ TAG_TYPE_NAME.to_int() }>, E>>, - ) -> ErasedEffective<'e, Flow, E> + ) -> ErasedEffective<'e, VisitResult, E> where 'ctx: 'this + 'visitor + 'hint + 'e, { @@ -316,9 +320,9 @@ where #[inline(always)] fn hint<'this, 'visitor, 'hint, 'e>( &'this mut self, - _visitor: &'visitor mut (dyn Tag<'ctx, tags::Map, E> + Send + Sync), + _visitor: DynVisitorWith<'visitor, 'ctx, TagProto<TagConst<{ TAG_MAP.to_int() }>, E>>, _hint: MetaHint<'hint, 'ctx, TagProto<TagConst<{ TAG_MAP.to_int() }>, E>>, - ) -> ErasedEffective<'e, Flow, E> + ) -> ErasedEffective<'e, VisitResult, E> where 'ctx: 'this + 'visitor + 'hint + 'e, { @@ -360,9 +364,9 @@ where #[inline(always)] fn hint<'this: 'e, 'visitor: 'e, 'hint: 'e, 'e>( &'this mut self, - visitor: &'visitor mut (dyn Tag<'ctx, tags::Struct, E> + Send + Sync), + visitor: DynVisitorWith<'visitor, 'ctx, TagProto<TagConst<{ TAG_STRUCT.to_int() }>, E>>, _hint: MetaHint<'hint, 'ctx, TagProto<TagConst<{ TAG_STRUCT.to_int() }>, E>>, - ) -> ErasedEffective<'e, Flow, E> + ) -> ErasedEffective<'e, VisitResult, E> where 'ctx: 'this + 'visitor + 'hint + 'e, { @@ -371,8 +375,9 @@ where #[inline(always)] |(visitor, noop_walker)| { visitor + .as_known() .visit(TagConst, noop_walker) - .map(|status| status.to_flow().unwrap_or(Flow::Continue)) + .map(|status| VisitResult::Control(status.to_flow().unwrap_or(Flow::Continue))) .cast() }, ) @@ -404,9 +409,9 @@ where #[inline(always)] fn hint<'this, 'visitor, 'hint, 'e>( &'this mut self, - _visitor: &'visitor mut (dyn Tag<'ctx, tags::TypeId, E> + Send + Sync), + _visitor: DynVisitorWith<'visitor, 'ctx, TagProto<TagConst<{ TAG_TYPE_ID.to_int() }>, E>>, _hint: MetaHint<'hint, 'ctx, TagProto<TagConst<{ TAG_TYPE_ID.to_int() }>, E>>, - ) -> ErasedEffective<'e, Flow, E> + ) -> ErasedEffective<'e, VisitResult, E> where 'ctx: 'this + 'visitor + 'hint + 'e, { @@ -452,9 +457,9 @@ where #[inline(always)] fn hint<'this: 'e, 'visitor: 'e, 'hint: 'e, 'e>( &'this mut self, - _visitor: &'visitor mut (dyn Tag<'ctx, TagDyn, E> + Send + Sync), + _visitor: DynVisitorWith<'visitor, 'ctx, TagProto<TagDyn, E>>, _hint: MetaHint<'hint, 'ctx, TagProto<TagDyn, E>>, - ) -> ErasedEffective<'e, Flow, E> + ) -> ErasedEffective<'e, VisitResult, E> where 'ctx: 'this + 'visitor + 'hint + 'e, { @@ -520,9 +525,9 @@ where #[inline(always)] fn hint<'this, 'visitor, 'hint, 'e>( &'this mut self, - _visitor: &'visitor mut (dyn Value<'ctx, BorrowedStaticHrt<I::T>, E> + Send + Sync), + _visitor: DynVisitorWith<'visitor, 'ctx, ValueProto<BorrowedStaticHrt<I::T>, E>>, _hint: MetaHint<'hint, 'ctx, ValueProto<BorrowedStaticHrt<I::T>, E>>, - ) -> ErasedEffective<'e, Flow, E> + ) -> ErasedEffective<'e, VisitResult, E> where 'ctx: 'this + 'visitor + 'hint + 'e, { @@ -554,18 +559,22 @@ where fn hint<'this: 'e, 'visitor: 'e, 'hint: 'e, 'e>( &'this mut self, - visitor: &'visitor mut (dyn Sequence<'ctx, E> + Send + Sync), + visitor: DynVisitorWith<'visitor, 'ctx, SequenceProto<E>>, _hint: MetaHint<'hint, 'ctx, SequenceProto<E>>, - ) -> ErasedEffective<'e, Flow, E> + ) -> ErasedEffective<'e, VisitResult, E> where 'ctx: 'this + 'visitor + 'hint + 'e, { E::as_ctx_map((self, visitor), |(this, visitor)| { visitor + .as_known() .visit(*this) - .map(|status| match status { - VisitResult::Skipped(_) => Flow::Continue, - VisitResult::Control(flow) => flow, + .map(|status| { + match status { + VisitResult::Skipped(_) => Flow::Continue, + VisitResult::Control(flow) => flow, + } + .into() }) .cast() }) 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, + } +} diff --git a/tests/builder_value.rs b/tests/builder_value.rs index 9f52252..b1d36de 100644 --- a/tests/builder_value.rs +++ b/tests/builder_value.rs @@ -20,12 +20,15 @@ fn value_builder_gives_value_protocol_as_hint() { // Expect the value builder to hint the value protocol with a owned i32. let mut walker = MockHintWalker::<ValueProto<OwnedStatic<i32>, Blocking>>::new(); - walker.expect_hint().once().returning(|visitor, ()| { + walker.expect_hint().once().returning(|mut visitor, ()| { // Fulfill the hint by visiting with a i32 value. - assert_eq!(visitor.visit(OwnedStatic(42)).value(), Flow::Done.into()); + assert_eq!( + visitor.as_known().visit(OwnedStatic(42)).value(), + Flow::Done.into() + ); // We are done as the walker. - Flow::Done + Flow::Done.into() }); // Request a hint from the i32 builder for what protocol to use. diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 5b35ce8..5c714e1 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -9,7 +9,7 @@ use std::{ sync::{Mutex, MutexGuard, OnceLock, RwLock}, }; -use treaty::effect::blocking::{BlockOn, Spin}; +use treaty::effect::blocking::Spin; pub mod builder; pub mod protocol; diff --git a/tests/common/protocol/hint.rs b/tests/common/protocol/hint.rs index 1ca94dc..89f53a6 100644 --- a/tests/common/protocol/hint.rs +++ b/tests/common/protocol/hint.rs @@ -3,7 +3,8 @@ use treaty::{ any::{any_trait, TypeName}, effect::{Effect, Effective, ErasedEffective}, protocol::{ - walker::hint::{Hint, HintMeta, HintProto, MetaHint, MetaKnown}, + visitor::VisitResult, + walker::hint::{DynVisitorWith, Hint, HintMeta, HintProto, MetaHint, MetaKnown}, DynVisitor, }, Flow, @@ -14,7 +15,7 @@ pub type KnownFactory<P> = mock! { pub HintWalker<P: HintMeta> { - pub fn hint<'a, 'b, 'c, 'ctx>(&'a mut self, visitor: &'b mut TypeName::T<'b, 'ctx, P>, hint: MetaHint<'c, 'ctx, P>) -> Flow; + pub fn hint<'a, 'b, 'c, 'ctx>(&'a mut self, visitor: DynVisitorWith<'b, 'ctx, P>, hint: MetaHint<'c, 'ctx, P>) -> VisitResult; pub fn known(&self) -> KnownFactory<P>; } @@ -30,9 +31,9 @@ any_trait! { impl<'ctx, P: HintMeta> Hint<'ctx, P> for MockHintWalker<P> { fn hint<'this, 'visitor, 'hint, 'e>( &'this mut self, - visitor: &'visitor mut TypeName::T<'visitor, 'ctx, P>, + visitor: DynVisitorWith<'visitor, 'ctx, P>, hint: MetaHint<'hint, 'ctx, P>, - ) -> ErasedEffective<'e, Flow, P::Effect> + ) -> ErasedEffective<'e, VisitResult, P::Effect> where 'ctx: 'this + 'visitor + 'hint + 'e, { diff --git a/tests/common/protocol/recoverable.rs b/tests/common/protocol/recoverable.rs index 1ccce92..c338b09 100644 --- a/tests/common/protocol/recoverable.rs +++ b/tests/common/protocol/recoverable.rs @@ -16,15 +16,9 @@ use treaty::{ Flow, Status, }; -pub type RecoverableScopeFactory<E> = - for<'a, 'ctx> fn( - &'ctx (), - DynRecoverableScope<'a, 'ctx, E>, - ) -> VisitResult<DynRecoverableScope<'a, 'ctx, E>>; - mock! { pub RecoverableVisitor<E> { - pub fn visit(&mut self) -> RecoverableScopeFactory<E>; + pub fn visit<'a, 'ctx>(&mut self, scope: DynRecoverableScope<'a, 'ctx, E>) -> VisitResult; } } @@ -39,8 +33,8 @@ impl<'ctx, E: Effect> Recoverable<'ctx, E> for MockRecoverableVisitor<E> { fn visit<'a>( &'a mut self, scope: DynRecoverableScope<'a, 'ctx, E>, - ) -> ErasedEffective<'a, VisitResult<DynRecoverableScope<'a, 'ctx, E>>, E> { - E::ready(self.visit()(&(), scope)) + ) -> ErasedEffective<'a, VisitResult, E> { + E::ready(self.visit(scope)) } } diff --git a/tests/common/protocol/request_hint.rs b/tests/common/protocol/request_hint.rs index dc57b5e..63c423d 100644 --- a/tests/common/protocol/request_hint.rs +++ b/tests/common/protocol/request_hint.rs @@ -7,12 +7,9 @@ use treaty::{ Flow, }; -pub type RequestHintFactory = - for<'a, 'ctx> fn(&'ctx (), DynWalker<'a, 'ctx>) -> VisitResult<DynWalker<'a, 'ctx>>; - mock! { pub RequestHintVisitor<E> { - pub fn request_hint(&mut self) -> RequestHintFactory; + pub fn request_hint<'a, 'ctx>(&mut self, walker: DynWalker<'a, 'ctx>) -> VisitResult; } } @@ -24,10 +21,13 @@ any_trait! { } impl<'ctx, E: Effect> RequestHint<'ctx, E> for MockRequestHintVisitor<E> { - fn request_hint<'a>( - &'a mut self, - walker: DynWalker<'a, 'ctx>, - ) -> ErasedEffective<'a, VisitResult<DynWalker<'a, 'ctx>>, E> { - E::ready(self.request_hint()(&(), walker)) + fn request_hint<'this: 'e, 'walker: 'e, 'e>( + &'this mut self, + walker: DynWalker<'walker, 'ctx>, + ) -> ErasedEffective<'e, VisitResult, E> + where + 'ctx: 'this + 'walker, + { + E::ready(self.request_hint(walker)) } } diff --git a/tests/common/protocol/sequence.rs b/tests/common/protocol/sequence.rs index c7e68b1..43e71db 100644 --- a/tests/common/protocol/sequence.rs +++ b/tests/common/protocol/sequence.rs @@ -13,14 +13,9 @@ use treaty::{ Flow, }; -pub type SequenceScopeFactory<E> = for<'a, 'ctx> fn( - &'ctx (), - DynSequenceScope<'a, 'ctx, E>, -) -> VisitResult<DynSequenceScope<'a, 'ctx, E>>; - mock! { pub SequenceVisitor<E> { - pub fn visit(&mut self) -> SequenceScopeFactory<E>; + pub fn visit<'a, 'ctx>(&mut self, scope: DynSequenceScope<'a, 'ctx, E>) -> VisitResult; } } @@ -35,8 +30,8 @@ impl<'ctx, E: Effect> Sequence<'ctx, E> for MockSequenceVisitor<E> { fn visit<'a: 'c, 'b: 'c, 'c>( &'a mut self, scope: DynSequenceScope<'b, 'ctx, E>, - ) -> ErasedEffective<'c, VisitResult<DynSequenceScope<'b, 'ctx, E>>, E> { - E::ready(self.visit()(&(), scope)) + ) -> ErasedEffective<'c, VisitResult, E> { + E::ready(self.visit(scope)) } } diff --git a/tests/common/protocol/tag.rs b/tests/common/protocol/tag.rs index 2dcd805..890157e 100644 --- a/tests/common/protocol/tag.rs +++ b/tests/common/protocol/tag.rs @@ -8,7 +8,7 @@ use treaty::{ mock! { pub TagVisitor<K: TagKind, E> { - pub fn visit<'a, 'ctx>(&'a mut self, kind: K, walker: DynWalkerObjSafe<'a, 'ctx, E>) -> VisitResult<()>; + pub fn visit<'a, 'ctx>(&'a mut self, kind: K, walker: DynWalkerObjSafe<'a, 'ctx, E>) -> VisitResult; } } @@ -25,10 +25,7 @@ impl<'ctx, K: TagKind, E: Effect> Tag<'ctx, K, E> for MockTagVisitor<K, E> { &'a mut self, kind: K, walker: DynWalkerObjSafe<'b, 'ctx, E>, - ) -> ErasedEffective<'c, VisitResult<DynWalkerObjSafe<'b, 'ctx, E>>, E> { - E::ready(match self.visit(kind, walker) { - VisitResult::Skipped(_) => VisitResult::Skipped(walker), - VisitResult::Control(flow) => VisitResult::Control(flow), - }) + ) -> ErasedEffective<'c, VisitResult, E> { + E::ready(self.visit(kind, walker)) } } diff --git a/tests/protocol_visitor_recoverable.rs b/tests/protocol_visitor_recoverable.rs index cde5ca1..2b8e6a1 100644 --- a/tests/protocol_visitor_recoverable.rs +++ b/tests/protocol_visitor_recoverable.rs @@ -9,10 +9,7 @@ use treaty::{ Flow, Status, }; -use crate::common::{ - builder::MockBuilder, - protocol::recoverable::{MockRecoverableScopeVisitor, RecoverableScopeFactory}, -}; +use crate::common::{builder::MockBuilder, protocol::recoverable::MockRecoverableScopeVisitor}; mod common; @@ -22,23 +19,21 @@ fn recoverable_can_be_visited() { let mut mock = MockRecoverableVisitor::<Blocking>::new(); // Expect a visit using the rescoverable protocol. - mock.expect_visit().once().return_const( - (|(), scope| { - let mut visitor = MockBuilder::<(), (), ()>::new(); + mock.expect_visit().once().returning(|scope| { + let mut visitor = MockBuilder::<(), (), ()>::new(); - // Expect that the visitor gets used. - visitor.expect_traits().times(2).return_const(None); + // Expect that the visitor gets used. + visitor.expect_traits().times(2).return_const(None); - // Attempt to walk once. - assert_eq!(scope.new_walk(DynVisitor(&mut visitor)).value(), Status::Ok); + // Attempt to walk once. + assert_eq!(scope.new_walk(DynVisitor(&mut visitor)).value(), Status::Ok); - // Attempt to walk twice. - assert_eq!(scope.new_walk(DynVisitor(&mut visitor)).value(), Status::Ok); + // Attempt to walk twice. + assert_eq!(scope.new_walk(DynVisitor(&mut visitor)).value(), Status::Ok); - // We are done. - VisitResult::Control(Flow::Done) - }) as RecoverableScopeFactory<Blocking>, - ); + // We are done. + VisitResult::Control(Flow::Done) + }); let visitor: &mut dyn Recoverable<Blocking> = &mut mock; diff --git a/tests/protocol_visitor_request_hint.rs b/tests/protocol_visitor_request_hint.rs index dee681d..098e24d 100644 --- a/tests/protocol_visitor_request_hint.rs +++ b/tests/protocol_visitor_request_hint.rs @@ -1,10 +1,7 @@ use std::any::TypeId; use common::{ - protocol::{ - hint::MockHintWalker, - request_hint::{MockRequestHintVisitor, RequestHintFactory}, - }, + protocol::{hint::MockHintWalker, request_hint::MockRequestHintVisitor}, walker::MockWalker, }; use mockall::predicate::eq; @@ -28,25 +25,23 @@ fn hints_can_be_requested() { let mut mock = MockRequestHintVisitor::<Blocking>::new(); // We will request a hint from the visitor. - mock.expect_request_hint().once().return_const( - (|_, mut walker| { - // Lookup the value protocol on the walker. - let obj = walker - .upcast_mut::<HintProto<ValueProto<OwnedStatic<i32>, Blocking>>>() - .unwrap(); - - // Get the known for the value protocol. - assert_eq!( - obj.known(&()).value(), - Ok(ValueKnown { - preview: Some(&OwnedStatic(42)) - }) - ); - - // We are done. - VisitResult::Control(Flow::Done) - }) as RequestHintFactory, - ); + mock.expect_request_hint().once().returning(|mut walker| { + // Lookup the value protocol on the walker. + let obj = walker + .upcast_mut::<HintProto<ValueProto<OwnedStatic<i32>, Blocking>>>() + .unwrap(); + + // Get the known for the value protocol. + assert_eq!( + obj.known(&()).value(), + Ok(ValueKnown { + preview: Some(&OwnedStatic(42)) + }) + ); + + // We are done. + VisitResult::Control(Flow::Done) + }); // This mock becomes the visitor. let visitor: &mut dyn RequestHint<Blocking> = &mut mock; diff --git a/tests/protocol_visitor_sequence.rs b/tests/protocol_visitor_sequence.rs index 732e090..bc29ea4 100644 --- a/tests/protocol_visitor_sequence.rs +++ b/tests/protocol_visitor_sequence.rs @@ -1,6 +1,6 @@ use std::any::TypeId; -use common::protocol::sequence::{MockSequenceScope, MockSequenceVisitor, SequenceScopeFactory}; +use common::protocol::sequence::{MockSequenceScope, MockSequenceVisitor}; use treaty::{ any::{OwnedStatic, TypeNameId}, effect::blocking::Blocking, @@ -20,23 +20,21 @@ fn sequence_has_scope_with_size_hint_and_next() { let mut mock = MockSequenceVisitor::<Blocking>::new(); // Expect a visit with the sequence protocol. - mock.expect_visit().once().return_const( - (|(), scope| { - // Get the size hint from the sequence scope. - assert_eq!(scope.size_hint().value(), (1, Some(1))); + mock.expect_visit().once().returning(|scope| { + // Get the size hint from the sequence scope. + assert_eq!(scope.size_hint().value(), (1, Some(1))); - let mut visitor = MockBuilder::<(), (), ()>::new(); + let mut visitor = MockBuilder::<(), (), ()>::new(); - // Expect the walker to lookup a trait. - visitor.expect_traits().once().return_const(None); + // Expect the walker to lookup a trait. + visitor.expect_traits().once().return_const(None); - // Get the next item in the sequence from the walker. - assert_eq!(scope.next(DynVisitor(&mut visitor)).value(), Flow::Done); + // Get the next item in the sequence from the walker. + assert_eq!(scope.next(DynVisitor(&mut visitor)).value(), Flow::Done); - // We are done. - VisitResult::Control(Flow::Done) - }) as SequenceScopeFactory<Blocking>, - ); + // We are done. + VisitResult::Control(Flow::Done) + }); // Everything goes throw the sequence protocol trait. let visitor: &mut dyn Sequence<Blocking> = &mut mock; diff --git a/tests/serde_deserializer.rs b/tests/serde_deserializer.rs new file mode 100644 index 0000000..4b7bc94 --- /dev/null +++ b/tests/serde_deserializer.rs @@ -0,0 +1,23 @@ +use serde_json::json; +use treaty::walkers::serde::deserializer::DeserializerWalker; +use treaty::BuildExt as _; + +mod common; + +#[test] +fn demo() { + let x = json!(true); + + let y = bool::build(DeserializerWalker::new(x)); + + assert!(y.unwrap()); +} + +#[test] +fn demo2() { + let x = json!(42); + + let y = u8::build(DeserializerWalker::new(x)); + + assert_eq!(y.unwrap(), 42); +} diff --git a/tests/walker_struct.rs b/tests/walker_struct.rs index 6cb6e36..5898475 100644 --- a/tests/walker_struct.rs +++ b/tests/walker_struct.rs @@ -12,11 +12,7 @@ use treaty::{ use crate::common::{ builder::{EmptyError, MockBuilder}, - protocol::{ - sequence::{MockSequenceVisitor, SequenceScopeFactory}, - tag::MockTagVisitor, - value::MockValueVisitor, - }, + protocol::{sequence::MockSequenceVisitor, tag::MockTagVisitor, value::MockValueVisitor}, }; mod common; @@ -107,81 +103,78 @@ fn sequence_of_field_values() { let mut visitor = MockSequenceVisitor::<Blocking>::new(); // Expect the sequence visitor to be used. - visitor.expect_visit().once().return_const( - (|_, scope| { - // The struct should have exactly 2 fields. - assert_eq!(scope.size_hint().value(), (2, Some(2))); - - // Get the first field value. - { - let mut visitor = MockBuilder::<(), (), EmptyError>::new(); - - // Expect a bool value for the field. - visitor - .expect_traits_mut() - .once() - .with(eq( - TypeNameId::of::<ValueProto<OwnedStatic<bool>, Blocking>>(), - )) - .returning(|_| { - let mut visitor = - MockValueVisitor::<OwnedStatic<bool>, Blocking>::new(); - - // Expect true. - visitor - .expect_visit() - .once() - .with(eq(OwnedStatic(true))) - .return_const(Flow::Done); - - Some(Box::new(visitor)) - }); - - assert_eq!( - scope - .next(Builder::<Blocking>::as_visitor(&mut visitor)) - .value(), - Flow::Continue - ); - } - - // Get the second field value. - { - let mut visitor = MockBuilder::<(), (), EmptyError>::new(); - - // Expect a i32 value. - visitor - .expect_traits_mut() - .once() - .with(eq( - TypeNameId::of::<ValueProto<OwnedStatic<i32>, Blocking>>(), - )) - .returning(|_| { - let mut visitor = - MockValueVisitor::<OwnedStatic<i32>, Blocking>::new(); - - // Expect a 42. - visitor - .expect_visit() - .once() - .with(eq(OwnedStatic(42))) - .return_const(Flow::Done); - - Some(Box::new(visitor)) - }); - - assert_eq!( - scope - .next(Builder::<Blocking>::as_visitor(&mut visitor)) - .value(), - Flow::Done - ); - } - - // We are done with the sequence of fields. - VisitResult::Control(Flow::Done) - }) as SequenceScopeFactory<Blocking>, - ); + visitor.expect_visit().once().returning(|scope| { + // The struct should have exactly 2 fields. + assert_eq!(scope.size_hint().value(), (2, Some(2))); + + // Get the first field value. + { + let mut visitor = MockBuilder::<(), (), EmptyError>::new(); + + // Expect a bool value for the field. + visitor + .expect_traits_mut() + .once() + .with(eq( + TypeNameId::of::<ValueProto<OwnedStatic<bool>, Blocking>>(), + )) + .returning(|_| { + let mut visitor = + MockValueVisitor::<OwnedStatic<bool>, Blocking>::new(); + + // Expect true. + visitor + .expect_visit() + .once() + .with(eq(OwnedStatic(true))) + .return_const(Flow::Done); + + Some(Box::new(visitor)) + }); + + assert_eq!( + scope + .next(Builder::<Blocking>::as_visitor(&mut visitor)) + .value(), + Flow::Continue + ); + } + + // Get the second field value. + { + let mut visitor = MockBuilder::<(), (), EmptyError>::new(); + + // Expect a i32 value. + visitor + .expect_traits_mut() + .once() + .with(eq( + TypeNameId::of::<ValueProto<OwnedStatic<i32>, Blocking>>(), + )) + .returning(|_| { + let mut visitor = MockValueVisitor::<OwnedStatic<i32>, Blocking>::new(); + + // Expect a 42. + visitor + .expect_visit() + .once() + .with(eq(OwnedStatic(42))) + .return_const(Flow::Done); + + Some(Box::new(visitor)) + }); + + assert_eq!( + scope + .next(Builder::<Blocking>::as_visitor(&mut visitor)) + .value(), + Flow::Done + ); + } + + // We are done with the sequence of fields. + VisitResult::Control(Flow::Done) + }); Some(Box::new(visitor)) }); |