all tests passing
31 files changed, 947 insertions, 740 deletions
@@ -10,6 +10,7 @@ readme = "README.md" # include = ["LICENSE-APACHE", "LICENSE-MIT", "README.md", "empty.rs"] [dependencies] +macro_rules_attribute = "0.2.0" futures = "0.3.30" pin-project = "1.1.5" # serde = { version = "1.0", default-features = false, optional = true } @@ -28,6 +29,10 @@ proptest = "1.4.0" serde_json = "1.0.114" tokio = { version = "1.36.0", features = ["full"] } +[profile.release] +opt-level = 3 +codegen-units = 1 + [profile.test.package.proptest] opt-level = 3 diff --git a/src/build/builders/core/bool.rs b/src/build/builders/core/bool.rs index 3cb60a6..60170e5 100644 --- a/src/build/builders/core/bool.rs +++ b/src/build/builders/core/bool.rs @@ -44,7 +44,7 @@ impl<'ctx, E: Effect> crate::Builder<'ctx, E> for Builder<E> { where Self: 'a, { - E::ready(self.0.ok_or(Error::Incomplete)).into_erased() + E::ready(self.0.ok_or(Error::Incomplete)) } #[inline(always)] @@ -52,7 +52,7 @@ impl<'ctx, E: Effect> crate::Builder<'ctx, E> for Builder<E> { where Self: 'a, { - E::ready(Self(None, PhantomData)).into_erased() + E::ready(Self(None, PhantomData)) } #[inline(always)] @@ -77,6 +77,6 @@ impl<'ctx, E: Effect> Value<'ctx, OwnedStatic<bool>, E> for Builder<E> { 'ctx: 'a, { self.0 = Some(value); - E::ready(Flow::Done.into()).into_erased() + E::ready(Flow::Done.into()) } } diff --git a/src/build/builders/core/struct.rs b/src/build/builders/core/struct.rs index b3983ab..b8ca483 100644 --- a/src/build/builders/core/struct.rs +++ b/src/build/builders/core/struct.rs @@ -1,9 +1,11 @@ -use core::{marker::PhantomData, ops::ControlFlow}; +use core::ops::ControlFlow; use crate::{ any::{OwnedStatic, TempBorrowedStatic, TempBorrowedStaticHrt, TypeName}, any_trait, - effect::{all_ctx, merge_ctx, Effect, Effective, ErasedEffective}, + effect::{ + Effect, EffectExt as _, Effective, EffectiveExt as _, ErasedEffective, ReadyExt as _, + }, hkt::Marker, protocol::{ visitor::{ @@ -12,7 +14,7 @@ use crate::{ }, DynVisitor, }, - Builder, BuilderTypes, DynWalkerObjSafe, Flow, + tri, Builder, BuilderTypes, DynWalkerObjSafe, Flow, }; use super::NoopVisitor; @@ -92,16 +94,14 @@ where where Self: 'a, { - I::new_builders(seed) - .map(|builders| { - Self { - builders, - // Start in tuple mode until a struct or map tag is visited. - mode: StructMode::Tuple, - _generics: Default::default(), - } - }) - .into_erased() + I::new_builders(seed).map(|builders| { + Self { + builders, + // Start in tuple mode until a struct or map tag is visited. + mode: StructMode::Tuple, + _generics: Default::default(), + } + }) } fn build<'a>(self) -> ErasedEffective<'a, Result<Self::Value, Self::Error>, E> @@ -109,11 +109,7 @@ where Self: 'a, { I::from_builders(self.builders) - .map(|builders| match builders { - Ok(value) => Ok(value), - Err(err) => Err(StructError { error: err }), - }) - .into_erased() + .map(|builders| builders.map_err(|err| StructError { error: err })) } fn as_visitor(&mut self) -> DynVisitor<'_, 'ctx> { @@ -144,19 +140,12 @@ where // This signals to go into map mode for the sequence. self.mode = StructMode::Map; - E::ready(NoopVisitor::new()) - .as_ctx( - all_ctx, - |noop, _| { - walker - .walk(DynVisitor(noop)) - .map(|x| x.to_continue().into()) - .into_erased() - }, - merge_ctx, - ) - .map(|(_, value)| value) - .into_erased() + E::ready(NoopVisitor::new()).as_ctx_map(|noop| { + walker + .walk(DynVisitor(noop)) + .map(|x| x.to_continue().into()) + .cast() + }) } } @@ -167,73 +156,55 @@ where E: Effect, { #[inline(always)] - fn visit<'a>( + fn visit<'a: 'c, 'b: 'c, 'c>( &'a mut self, - scope: DynSequenceScope<'a, 'ctx, E>, - ) -> ErasedEffective<'a, VisitResult<DynSequenceScope<'a, 'ctx, E>>, E> { + scope: DynSequenceScope<'b, 'ctx, E>, + ) -> ErasedEffective<'c, VisitResult<DynSequenceScope<'b, 'ctx, E>>, E> + where + 'ctx: 'c, + { match self.mode { StructMode::Tuple => { - E::ready((self, scope, 0)) - .r#loop(|(this, scope, index)| { - if let Some(marker) = I::marker_from_index(*index) { - // Select the visitor for this field. - let visitor = I::as_visitor(marker, &mut this.builders); - - // For rust analyzer. - let scope: &mut DynSequenceScope<'_, '_, E> = scope; - - scope - .next(visitor) - .map(|flow| match flow { - Flow::Continue => { - *index += 1; - - ControlFlow::Continue(()) - } - Flow::Err => { - ControlFlow::Break(VisitResult::Control(Flow::Err)) - } - Flow::Done => { - ControlFlow::Break(VisitResult::Control(Flow::Done)) - } - }) - .into_erased() - } else { - E::ready(ControlFlow::Break(VisitResult::Control(Flow::Done))) - .into_erased() - } - }) - .map(|(_, value)| value) - .into_erased() + // Tuple-like is based on the index of the field. + let mut index = 0; + + // Loop through all the fields getting a value for each one. + E::repeat_map((self, scope), |(this, scope)| { + // Get the marker for the field at this index. + let marker = tri!(I::marker_from_index(index)); + + // Move to the next field for the next iteration. + index += 1; + + // Select the visitor for this field. + let visitor = I::as_visitor(marker, &mut this.builders); + + // Visit the next item in the sequence. + scope.next(visitor).map(Flow::to_control_flow).cast() + }) } - StructMode::Map => E::ready((self, scope)) - .r#loop(|(this, scope)| { - let visitor = FieldVisitor::<I, M, E> { - builders: &mut this.builders, - marker: None, - _marker: Default::default(), - }; - - E::ready((scope, visitor)) - .as_ctx( - all_ctx, - |(scope, visitor), _| { - let scope: &mut DynSequenceScope<'_, '_, _> = scope; - - scope.next(DynVisitor(visitor)).into_erased() - }, - merge_ctx, - ) - .map(|(_, result)| match result { - Flow::Continue => ControlFlow::Continue(()), - Flow::Err => ControlFlow::Break(VisitResult::Control(Flow::Err)), - Flow::Done => ControlFlow::Break(VisitResult::Control(Flow::Done)), - }) - .into_erased() + StructMode::Map => { + // A visitor that knows how to use field names. + let visitor = FieldVisitor::<I, M, E> { + builders: &mut self.builders, + marker: None, + _marker: Default::default(), + }; + + // Loop through all the elements in the sequence. + // Each key value pair will be mapped to a field. + E::repeat_map((visitor, scope), |(visitor, scope)| { + E::as_ctx_map((scope, visitor), |(scope, visitor)| { + // Visit the next element of the sequence. + // When there are no more items in the sequence then the loop ends. + scope.next(DynVisitor(*visitor)).cast() + }) + .map(Flow::to_control_flow) + .cast() }) - .map(|(_, value)| value) - .into_erased(), + } } + .map(Into::into) } } @@ -277,16 +248,11 @@ where }; E::ready((self, visitor, walker)) - .as_ctx( - all_ctx, - |(_, visitor, walker), _| walker.walk(DynVisitor(visitor)).into_erased(), - merge_ctx, - ) + .as_ctx(|(_, visitor, walker)| walker.walk(DynVisitor(visitor)).cast()) .map(|((this, visitor, _), flow)| { this.marker = visitor.field_marker; flow.to_continue().into() }) - .into_erased() } } @@ -321,7 +287,7 @@ where { self.field_marker = I::marker_from_index(index); - E::ready(VisitResult::Control(Flow::Done)).into_erased() + E::ready(VisitResult::Control(Flow::Done)) } } @@ -341,7 +307,7 @@ where { self.field_marker = I::marker_from_name(name); - E::ready(VisitResult::Control(Flow::Done)).into_erased() + E::ready(VisitResult::Control(Flow::Done)) } } @@ -361,6 +327,6 @@ where { self.field_marker = I::marker_from_name(name); - E::ready(VisitResult::Control(Flow::Done)).into_erased() + E::ready(VisitResult::Control(Flow::Done)) } } diff --git a/src/effect.rs b/src/effect.rs index 0f85ee3..458ed8a 100644 --- a/src/effect.rs +++ b/src/effect.rs @@ -3,7 +3,7 @@ pub mod blocking; use core::{future::Future, ops::ControlFlow}; -use crate::{higher_ranked_trait, higher_ranked_type, hkt::Marker}; +use crate::{higher_ranked_trait, higher_ranked_type, hkt::Marker, never::Never}; // Goal: code using the module shouldn't care if the underlying environment is async or not. // That is the API should allow yielding without forcing it. @@ -106,164 +106,364 @@ pub trait Effect: Join<Effect = Self> + TryJoin<Effect = Self> + Ss + Sized + 's F::Output: Ss + 'a; } -pub fn all_ctx<T>(x: T) -> (T, ()) { - (x, ()) +pub trait ReadyExt<'lt, Eff: Effective<'lt, Output = Self>>: Ss + Sized { + fn ready(self) -> Eff; } -pub fn merge_ctx<C, T>(ctx: C, x: T) -> (C, T) { - (ctx, x) +impl<'lt, Eff: Effective<'lt, Output = T>, T: Ss> ReadyExt<'lt, Eff> for T { + fn ready(self) -> Eff { + Eff::ready(self) + } +} + +pub trait ConvertShort<T> { + fn convert_short(short: Self) -> T; } -pub trait ShortCircuit { - type Normal: Ss; +pub trait FromShort<S = <Self as ShortCircuit>::Short> { + fn from_short(short: S) -> Self; +} - type Short: Ss; +impl<T, S> FromShort<S> for T +where + S: ConvertShort<T>, +{ + fn from_short(short: S) -> Self { + S::convert_short(short) + } +} - type NormalWith<T: Ss>: ShortCircuit<Normal = T, Short = Self::Short> + Ss; +pub trait ShortCircuit: FromShort { + type Output; + type Short; - fn map<F, R: Ss>(self, f: F) -> Self::NormalWith<R> - where - F: FnOnce(Self::Normal) -> R; + fn from_output(output: Self::Output) -> Self; + fn branch(self) -> ControlFlow<Self::Short, Self::Output>; +} - fn or_else<F>(self, f: F) -> Self - where - F: FnOnce(Self::Short) -> Self; +pub fn effective_from_short<'lt, Eff, S>(short: S) -> Eff +where + Eff: Effective<'lt>, + Eff::Output: FromShort<S>, +{ + Eff::ready(FromShort::from_short(short)) +} + +impl<T> ConvertShort<Option<T>> for Option<Never> { + fn convert_short(short: Option<Never>) -> Option<T> { + match short { + None => None, + Some(_) => unreachable!(), + } + } } -impl<T: Ss> ShortCircuit for Option<T> { - type Normal = T; +impl<T> ShortCircuit for Option<T> { + type Output = T; - type Short = (); + type Short = Option<Never>; - type NormalWith<U: Ss> = Option<U>; + fn branch(self) -> ControlFlow<Self::Short, Self::Output> { + match self { + Some(v) => ControlFlow::Continue(v), + None => ControlFlow::Break(None), + } + } - fn map<F, R: Ss>(self, f: F) -> Self::NormalWith<R> - where - F: FnOnce(Self::Normal) -> R, - { - Option::map(self, f) + fn from_output(output: Self::Output) -> Self { + Some(output) } +} - fn or_else<F>(self, f: F) -> Self - where - F: FnOnce(Self::Short) -> Self, - { - Option::or_else(self, || f(())) +impl<T, E> ConvertShort<Result<T, E>> for Result<Never, E> { + fn convert_short(short: Result<Never, E>) -> Result<T, E> { + match short { + Err(err) => Err(err), + Ok(_) => unreachable!(), + } } } -impl<T: Ss, E: Ss> ShortCircuit for Result<T, E> { - type Normal = T; +impl<T, E> ShortCircuit for Result<T, E> { + type Output = T; + + type Short = Result<Never, E>; + + fn branch(self) -> ControlFlow<Self::Short, Self::Output> { + match self { + Ok(v) => ControlFlow::Continue(v), + Err(err) => ControlFlow::Break(Err(err)), + } + } + + fn from_output(output: Self::Output) -> Self { + Ok(output) + } +} - type Short = E; +#[macro_export] +macro_rules! tri { + ($expr:expr $(,)?) => { + match $crate::effect::ShortCircuit::branch($expr) { + ::core::ops::ControlFlow::Continue(val) => val, + ::core::ops::ControlFlow::Break(short) => { + return $crate::effect::effective_from_short(short); + } + } + }; +} +pub use tri; - type NormalWith<U: Ss> = Result<U, E>; +pub trait EffectExt: Effect { + fn repeat_map<'ctx, 'wrap, I: Ss + 'wrap, U: Ss, F: Ss>( + input: I, + f: F, + ) -> ErasedEffective<'wrap, U, Self> + where + F: for<'temp> FnMut(&'temp mut I) -> ErasedEffective<'temp, ControlFlow<U>, Self, &'ctx ()>, + 'ctx: 'wrap, + { + Self::ready(input).repeat_map(f) + } - fn map<F, R: Ss>(self, f: F) -> Self::NormalWith<R> + #[inline(always)] + fn as_ctx<'ctx, 'wrap, I: Ss + 'wrap, U: Ss, F: Ss>( + input: I, + f: F, + ) -> ErasedEffective<'wrap, (I, U), Self> where - F: FnOnce(Self::Normal) -> R, + F: for<'temp> FnOnce(&'temp mut I) -> ErasedEffective<'temp, U, Self, &'ctx ()>, + 'ctx: 'wrap, { - Result::map(self, f) + Self::ready(input).as_ctx(f) } - fn or_else<F>(self, f: F) -> Self + fn as_ctx_map<'ctx, 'wrap, I: Ss + 'wrap, U: Ss, F: Ss>( + input: I, + f: F, + ) -> ErasedEffective<'wrap, U, Self> where - F: FnOnce(Self::Short) -> Self, + F: for<'temp> FnOnce(&'temp mut I) -> ErasedEffective<'temp, U, Self, &'ctx ()>, + 'ctx: 'wrap, { - Result::or_else(self, f) + Self::ready(input).as_ctx_map(f) } } +impl<T: Effect> EffectExt for T {} + pub trait EffectiveExt<'lt>: Effective<'lt> { - fn map_normal<'wrap, F: Ss, R: Ss>( - self, - f: F, - ) -> ErasedEffective<'wrap, <Self::Output as ShortCircuit>::NormalWith<R>, Self::Effect> + fn remove_ctx<'wrap, Ctx: Ss, T: Ss>(self) -> ErasedEffective<'wrap, T, Self::Effect> where - F: FnOnce(<Self::Output as ShortCircuit>::Normal) -> R, - Self::Output: ShortCircuit, - 'lt: 'wrap; + Self: Effective<'lt, Output = (Ctx, T)>, + 'lt: 'wrap, + { + self.map(|(_, value)| value) + } - fn or_else<'wrap, F: Ss>(self, f: F) -> ErasedEffective<'wrap, Self::Output, Self::Effect> + fn map<'wrap, U: Ss, F: Ss>(self, f: F) -> ErasedEffective<'wrap, U, Self::Effect> where - F: FnOnce(<Self::Output as ShortCircuit>::Short) -> Self::Output, - Self::Output: ShortCircuit, - 'lt: 'wrap; + F: FnOnce(Self::Output) -> U, + 'lt: 'wrap, + { + self.r#do( + |v| ((), ControlFlow::<_, Never>::Break(f(v))), + |_, _| unreachable!(), + |_, _: Never| unreachable!(), + |_, _: &mut Never| unreachable!(), + |_, _, _: Never| unreachable!(), + |_, _, v| v, + ) + } - fn demo(self) + fn then<'wrap, U: Ss, F: Ss>(self, f: F) -> ErasedEffective<'wrap, U, Self::Effect> where - Self: Effective<'lt, Output = (i32, i32)>; + F: FnOnce(Self::Output) -> ErasedEffective<'wrap, U, Self::Effect>, + 'lt: 'wrap, + { + self.r#do( + |v| ((), ControlFlow::Continue(v)), + |_, v| f(v).cast(), + |_, v| ControlFlow::<_, Never>::Break(v), + |_, _| unreachable!(), + |_, _, _: Never| unreachable!(), + |_, _, v| v, + ) + } - fn as_ctx_or_else<'ctx: 'lt, 'wrap, T: Ss, U: Ss, F: Ss>( - self, - f: F, - ) -> ErasedEffective<'wrap, (T, Option<U>), Self::Effect> + fn or_else<'wrap, U: Ss, F: Ss>(self, f: F) -> ErasedEffective<'wrap, Option<U>, Self::Effect> where - Self: Effective<'lt, Output = (T, Option<U>)>, - F: for<'b> FnOnce(&'b mut T) -> ErasedEffective<'b, Option<U>, Self::Effect, &'ctx ()>, - 'lt: 'wrap; -} + Self: Effective<'lt, Output = Option<U>>, + F: FnOnce() -> ErasedEffective<'wrap, Option<U>, Self::Effect>, + 'lt: 'wrap, + { + self.r#do( + |v| { + ( + (), + match v { + Some(v) => ControlFlow::Break(Some(v)), + None => ControlFlow::Continue(()), + }, + ) + }, + |_, _| f().cast(), + |_, v| ControlFlow::<_, Never>::Break(v), + |_, _| unreachable!(), + |_, _, _: Never| unreachable!(), + |_, _, v| v, + ) + } -impl<'lt, T: Effective<'lt>> EffectiveExt<'lt> for T { - fn map_normal<'wrap, F: Ss, R: Ss>( + #[inline(always)] + fn as_ctx<'ctx: 'lt, 'wrap, U: Ss, F: Ss>( self, f: F, - ) -> ErasedEffective<'wrap, <Self::Output as ShortCircuit>::NormalWith<R>, Self::Effect> + ) -> ErasedEffective<'wrap, (Self::Output, U), Self::Effect> where - F: FnOnce(<Self::Output as ShortCircuit>::Normal) -> R, - Self::Output: ShortCircuit, + F: for<'temp> FnOnce( + &'temp mut Self::Output, + ) -> ErasedEffective<'temp, U, Self::Effect, &'ctx ()>, 'lt: 'wrap, { - self.map(move |value| value.map(f)) + self.r#do( + |ctx| (ctx, ControlFlow::Continue(())), + #[inline(always)] + |ctx, _| f(ctx).cast(), + |_, v| ControlFlow::<_, Never>::Break(v), + |_, _| unreachable!(), + |_, _, _: Never| unreachable!(), + |ctx, _, v| (ctx, v), + ) } - fn or_else<'wrap, F: Ss>(self, f: F) -> ErasedEffective<'wrap, Self::Output, Self::Effect> + fn as_ctx_map<'ctx: 'lt, 'wrap, U: Ss, F: Ss>( + self, + f: F, + ) -> ErasedEffective<'wrap, U, Self::Effect> where - F: FnOnce(<Self::Output as ShortCircuit>::Short) -> Self::Output, - Self::Output: ShortCircuit, + F: for<'temp> FnOnce( + &'temp mut Self::Output, + ) -> ErasedEffective<'temp, U, Self::Effect, &'ctx ()>, 'lt: 'wrap, { - self.map(move |value| value.or_else(f)) + self.r#do( + |ctx| (ctx, ControlFlow::Continue(())), + |ctx, _| f(ctx).cast(), + |_, v| ControlFlow::<_, Never>::Break(v), + |_, _| unreachable!(), + |_, _, _: Never| unreachable!(), + |_, _, v| v, + ) } - fn demo(self) + fn as_ctx_or_else<'ctx: 'lt, 'wrap, Ctx: Ss, U: Ss, F: Ss>( + self, + f: F, + ) -> ErasedEffective<'wrap, (Ctx, Option<U>), Self::Effect> where - Self: Effective<'lt, Output = (i32, i32)>, + Self: Effective<'lt, Output = (Ctx, Option<U>)>, + F: for<'temp> FnOnce( + &'temp mut Ctx, + ) -> ErasedEffective<'temp, Option<U>, Self::Effect, &'ctx ()>, + 'lt: 'wrap, { - self.map(|(a, b)| a + b); + self.r#do( + |(ctx, v)| { + ( + ctx, + match v { + Some(v) => ControlFlow::Break(Some(v)), + None => ControlFlow::Continue(()), + }, + ) + }, + |ctx, ()| f(ctx).cast(), + |_, v| ControlFlow::<_, Never>::Break(v), + |_, _| unreachable!(), + |_, _, _: Never| unreachable!(), + |ctx, _, v| (ctx, v), + ) } - fn as_ctx_or_else<'ctx: 'lt, 'wrap, N: Ss, U: Ss, F: Ss>( + fn as_ctx_or_else_map<'ctx: 'lt, 'wrap, Ctx: Ss, U: Ss, F: Ss>( self, f: F, - ) -> ErasedEffective<'wrap, (N, Option<U>), Self::Effect> + ) -> ErasedEffective<'wrap, Option<U>, Self::Effect> where - Self: Effective<'lt, Output = (N, Option<U>)>, - F: for<'b> FnOnce(&'b mut N) -> ErasedEffective<'b, Option<U>, Self::Effect, &'ctx ()>, + Self: Effective<'lt, Output = (Ctx, Option<U>)>, + F: for<'temp> FnOnce( + &'temp mut Ctx, + ) -> ErasedEffective<'temp, Option<U>, Self::Effect, &'ctx ()>, 'lt: 'wrap, { - self.as_ctx( - // Replace everything in Effective with just the loop construct which can skip - // the as_ctx body. - all_ctx, - |(ctx, value), _| { - if value.is_some() { - <Self::Effect as Effect>::ready(None).into_erased() - } else { - f(ctx) - } - }, - |(ctx, a), b| match (a, b) { - (None, None) => (ctx, None), - (Some(v), None) | (None, Some(v)) => (ctx, Some(v)), - (Some(_), Some(_)) => unreachable!(), + self.r#do( + |(ctx, v)| { + ( + ctx, + match v { + Some(v) => ControlFlow::Break(Some(v)), + None => ControlFlow::Continue(()), + }, + ) }, + |ctx, ()| f(ctx).cast(), + |_, v| ControlFlow::<_, Never>::Break(v), + |_, _| unreachable!(), + |_, _, _: Never| unreachable!(), + |_, _, v| v, + ) + } + + fn repeat<'ctx: 'lt, 'wrap, U: Ss, F: Ss>( + self, + mut f: F, + ) -> ErasedEffective<'wrap, (Self::Output, U), Self::Effect> + where + F: for<'temp> FnMut( + &'temp mut Self::Output, + ) + -> ErasedEffective<'temp, ControlFlow<U>, Self::Effect, &'ctx ()>, + 'lt: 'wrap, + { + self.r#do( + |ctx| (ctx, ControlFlow::Continue(())), + |_, _| <Self::Effect as Effect>::ready(()).cast(), + |_, _| ControlFlow::Continue(()), + |ctx, _| f(ctx).cast(), + |_, _, v| v, + |ctx, _, v| (ctx, v), + ) + } + + fn repeat_map<'ctx: 'lt, 'wrap, U: Ss, F: Ss>( + self, + mut f: F, + ) -> ErasedEffective<'wrap, U, Self::Effect> + where + F: for<'temp> FnMut( + &'temp mut Self::Output, + ) + -> ErasedEffective<'temp, ControlFlow<U>, Self::Effect, &'ctx ()>, + 'lt: 'wrap, + { + self.r#do( + |ctx| (ctx, ControlFlow::Continue(())), + |_, _| <Self::Effect as Effect>::ready(()).cast(), + |_, _| ControlFlow::Continue(()), + |ctx, _| f(ctx).cast(), + |_, _, v| v, + |_, _, v| v, ) } } -pub trait Effective<'lt>: Ss + 'lt { - fn into_erased<B>(self) -> ErasedEffective<'lt, Self::Output, Self::Effect, B>; +impl<'lt, T: Effective<'lt>> EffectiveExt<'lt> for T {} + +pub trait Effective<'lt>: Sized + Ss + 'lt { + fn cast<'wrap, B>(self) -> ErasedEffective<'wrap, Self::Output, Self::Effect, B> + where + 'lt: 'wrap; /// The effect the effective belongs to. type Effect: Effect; @@ -277,36 +477,49 @@ pub trait Effective<'lt>: Ss + 'lt { /// Convert the effective into a general future for use in async. fn into_future(self) -> Self::IntoFuture; - fn map<'wrap, T: Ss, F: Ss>(self, cb: F) -> ErasedEffective<'wrap, T, Self::Effect> - where - F: FnOnce(Self::Output) -> T, - 'lt: 'wrap; - - fn then<'wrap, T: Ss, F: Ss>(self, cb: F) -> ErasedEffective<'wrap, T, Self::Effect> - where - F: FnOnce(Self::Output) -> ErasedEffective<'wrap, T, Self::Effect>, - 'lt: 'wrap; - - fn as_ctx<'ctx: 'lt, 'wrap, T: Ss, S: Ss, C: Ss, U: Ss, F: Ss, A: Ss, R: Ss>( - self, - split: S, - cb: F, - after: A, - ) -> ErasedEffective<'wrap, R, Self::Effect> - where - S: FnOnce(Self::Output) -> (C, U), - F: for<'b> FnOnce(&'b mut C, U) -> ErasedEffective<'b, T, Self::Effect, &'ctx ()>, - A: FnOnce(C, T) -> R, - 'lt: 'wrap; - - fn r#loop<'ctx: 'lt, 'wrap, T: Ss, F: Ss>( + fn ready(value: Self::Output) -> Self; + + fn r#do< + 'ctx: 'lt, + 'wrap, + Pre: Ss, + Ctx: Ss, + Owned: Ss, + First: Ss, + FirstOutput: Ss, + FirstPost: Ss, + Done: Ss, + Extra: Ss, + Repeat: Ss, + RepeatOutput: Ss, + RepeatPost: Ss, + Post: Ss, + Return: Ss, + >( self, - cb: F, - ) -> ErasedEffective<'wrap, (Self::Output, T), Self::Effect> + pre: Pre, + first: First, + first_post: FirstPost, + repeat: Repeat, + repeat_post: RepeatPost, + post: Post, + ) -> ErasedEffective<'wrap, Return, Self::Effect> where - F: for<'b> FnMut( - &'b mut Self::Output, - ) -> ErasedEffective<'b, ControlFlow<T>, Self::Effect, &'ctx ()>, + Pre: FnOnce(Self::Output) -> (Ctx, ControlFlow<Done, Owned>), + First: for<'temp> FnOnce( + &'temp mut Ctx, + Owned, + ) + -> ErasedEffective<'temp, FirstOutput, Self::Effect, &'wrap ()>, + FirstPost: for<'temp> FnOnce(&'temp mut Ctx, FirstOutput) -> ControlFlow<Done, Extra>, + Repeat: for<'temp> FnMut( + &'temp mut Ctx, + &'temp mut Extra, + ) + -> ErasedEffective<'temp, RepeatOutput, Self::Effect, &'wrap ()>, + RepeatPost: + for<'temp> FnMut(&'temp mut Ctx, &'temp mut Extra, RepeatOutput) -> ControlFlow<Done>, + Post: FnOnce(Ctx, Option<Extra>, Done) -> Return, 'lt: 'wrap; } @@ -502,3 +715,19 @@ where <E as TryJoin>::two(self) } } + +impl<'lt, E: Effect, F0, F1, F2, T0, T1, T2> TryJoinable<'lt, E> for (F0, F1, F2) +where + F0: FnOnce() -> T0 + Ss + 'lt, + F1: FnOnce() -> T1 + Ss + 'lt, + F2: FnOnce() -> T2 + Ss + 'lt, + T0: TryEffective<'lt, Effect = E>, + T1: TryEffective<'lt, Err = T0::Err, Effect = E>, + T2: TryEffective<'lt, Err = T0::Err, Effect = E>, +{ + type Output = <E as TryJoin>::Three<'lt, T0, T1, T2>; + + fn join(self) -> Self::Output { + <E as TryJoin>::three(self) + } +} diff --git a/src/effect/blocking.rs b/src/effect/blocking.rs index 11c3b7d..c504445 100644 --- a/src/effect/blocking.rs +++ b/src/effect/blocking.rs @@ -35,17 +35,10 @@ impl<T: Ss, B: BlockOn> Erased::Hkt<T, Blocking<B>> for Value<T, B> { impl<B: BlockOn> Effect for Blocking<B> { type Erased<T: Ss> = Value<T, B>; - // type Ready<'a, T: Ss + 'a> = Value<T, B>; - fn ready<'a, T: Ss + 'a>(value: T) -> ErasedEffective<'a, T, Self> { Value(value, Default::default()) } - // type FromFuture<'a, F: Ss + 'a> = Value<F::Output, B> - // where - // F: Future, - // F::Output: Ss + 'a; - fn from_future<'a, F: Ss + 'a>(future: F) -> ErasedEffective<'a, F::Output, Self> where F: Future, @@ -56,7 +49,7 @@ impl<B: BlockOn> Effect for Blocking<B> { } impl<'lt, U: Ss + 'lt, B: BlockOn> Effective<'lt> for Value<U, B> { - fn into_erased<X>(self) -> ErasedEffective<'lt, Self::Output, Self::Effect, X> { + fn cast<'wrap, X>(self) -> ErasedEffective<'wrap, Self::Output, Self::Effect, X> { self } @@ -70,74 +63,74 @@ impl<'lt, U: Ss + 'lt, B: BlockOn> Effective<'lt> for Value<U, B> { core::future::ready(self.0) } - // type Loop<'ctx: 'a, 'a, T: Ss + 'a, F: Ss + 'a> - // = Value<(U, T), B> - // where - // F: for<'b> FnMut(&'b mut Self::Output) -> ErasedEffective<'b, ControlFlow<T>, Self::Effect>, - // 'lt: 'a; - - fn r#loop<'ctx: 'lt, 'wrap, T: Ss, F: Ss>( + #[inline(always)] + fn r#do< + 'ctx: 'lt, + 'wrap, + Pre: Ss, + Ctx: Ss, + Owned: Ss, + First: Ss, + FirstOutput: Ss, + FirstPost: Ss, + Done: Ss, + Extra: Ss, + Repeat: Ss, + RepeatOutput: Ss, + RepeatPost: Ss, + Post: Ss, + Return: Ss, + >( self, - mut cb: F, - ) -> ErasedEffective<'wrap, (Self::Output, T), Self::Effect> + pre: Pre, + first: First, + first_post: FirstPost, + mut repeat: Repeat, + mut repeat_post: RepeatPost, + post: Post, + ) -> ErasedEffective<'wrap, Return, Self::Effect> where - F: for<'b> FnMut(&'b mut Self::Output) -> ErasedEffective<'b, ControlFlow<T>, Self::Effect>, - { - let mut this = self.0; - - loop { - if let ControlFlow::Break(value) = cb(&mut this).0 { - return Value((this, value), Default::default()); - } - } - } - - // type Map<'a, T: Ss + 'a, F: Ss + 'a> = Value<T, B> - // where - // F: FnOnce(Self::Output) -> T, - // 'lt: 'a; - - fn map<'wrap, T: Ss, F: Ss>(self, cb: F) -> ErasedEffective<'wrap, T, Self::Effect> - where - F: FnOnce(Self::Output) -> T, + Pre: FnOnce(Self::Output) -> (Ctx, ControlFlow<Done, Owned>), + First: for<'b> FnOnce( + &'b mut Ctx, + Owned, + ) -> ErasedEffective<'b, FirstOutput, Self::Effect, &'wrap ()>, + FirstPost: for<'b> FnOnce(&'b mut Ctx, FirstOutput) -> ControlFlow<Done, Extra>, + Repeat: for<'b> FnMut( + &'b mut Ctx, + &'b mut Extra, + ) + -> ErasedEffective<'b, RepeatOutput, Self::Effect, &'wrap ()>, + RepeatPost: for<'b> FnMut(&'b mut Ctx, &'b mut Extra, RepeatOutput) -> ControlFlow<Done>, + Post: FnOnce(Ctx, Option<Extra>, Done) -> Return, 'lt: 'wrap, { - Value(cb(self.0), Default::default()) - } - - // type Then<'a, T: Ss + 'a, V: Ss + 'a, F: Ss + 'a> = Value<T, B> - // where - // F: FnOnce(Self::Output) -> V, - // V: Effective<'a, Output = T, Effect = Self::Effect>, - // 'lt: 'a; + let (ctx, done, extra) = match pre(self.0) { + (mut ctx, ControlFlow::Continue(owned)) => { + let first_output = first(&mut ctx, owned).0; + + let (done, extra) = match first_post(&mut ctx, first_output) { + ControlFlow::Continue(mut extra) => loop { + let repeat_output = repeat(&mut ctx, &mut extra).0; + + match repeat_post(&mut ctx, &mut extra, repeat_output) { + ControlFlow::Continue(()) => {} + ControlFlow::Break(done) => break (done, Some(extra)), + } + }, + ControlFlow::Break(done) => (done, None), + }; + + (ctx, done, extra) + } + (ctx, ControlFlow::Break(done)) => (ctx, done, None), + }; - fn then<'a, T: Ss, F: Ss>(self, cb: F) -> ErasedEffective<'a, T, Self::Effect> - where - F: FnOnce(Self::Output) -> ErasedEffective<'a, T, Self::Effect>, - 'lt: 'a, - { - cb(self.0).into_erased::<()>() + Value(post(ctx, extra, done), Default::default()) } - // type AsCtx<'ctx: 'a, 'a, T: Ss + 'a, F: Ss + 'a> = Value<(U, T), B> - // where - // F: for<'b> FnOnce(&'b mut Self::Output) -> ErasedEffective<'b, T, Self::Effect, (&'b mut Self::Output, &'ctx ())>, - // 'lt: 'a; - - fn as_ctx<'ctx: 'lt, 'wrap, T: Ss, S: Ss, C: Ss, K: Ss, F: Ss, A: Ss, R: Ss>( - self, - split: S, - cb: F, - after: A, - ) -> ErasedEffective<'wrap, R, Self::Effect> - where - S: FnOnce(Self::Output) -> (C, K), - F: for<'b> FnOnce(&'b mut C, K) -> ErasedEffective<'b, T, Self::Effect>, - A: FnOnce(C, T) -> R, - { - let (mut this, owned) = split(self.0); - let result = cb(&mut this, owned).0; - Value(after(this, result), Default::default()) + fn ready(value: Self::Output) -> Self { + Value(value, Default::default()) } } @@ -160,8 +153,8 @@ impl<B: BlockOn> Join for Blocking<B> { T0: Effective<'a, Effect = Self::Effect>, T1: Effective<'a, Effect = Self::Effect>, { - let v0 = cb.0.into_erased::<()>().0; - let v1 = cb.1.into_erased::<()>().0; + let v0 = cb.0.cast::<()>().0; + let v1 = cb.1.cast::<()>().0; Value((v0, v1), Default::default()) } @@ -174,9 +167,9 @@ impl<B: BlockOn> Join for Blocking<B> { T1: Effective<'a, Effect = Self::Effect>, T2: Effective<'a, Effect = Self::Effect>, { - let v0 = cb.0.into_erased::<()>().0; - let v1 = cb.1.into_erased::<()>().0; - let v2 = cb.2.into_erased::<()>().0; + let v0 = cb.0.cast::<()>().0; + let v1 = cb.1.cast::<()>().0; + let v2 = cb.2.cast::<()>().0; Value((v0, v1, v2), Default::default()) } @@ -205,12 +198,12 @@ impl<B: BlockOn> TryJoin for Blocking<B> { F0: FnOnce() -> T0, F1: FnOnce() -> T1, { - let v0 = match (cb.0)().into_erased::<()>().0 { + let v0 = match (cb.0)().cast::<()>().0 { Ok(v) => v, Err(err) => return Value(Err(err), Default::default()), }; - let v1 = match (cb.1)().into_erased::<()>().0 { + let v1 = match (cb.1)().cast::<()>().0 { Ok(v) => v, Err(err) => return Value(Err(err), Default::default()), }; @@ -229,17 +222,17 @@ impl<B: BlockOn> TryJoin for Blocking<B> { F1: FnOnce() -> T1, F2: FnOnce() -> T2, { - let v0 = match (cb.0)().into_erased::<()>().0 { + let v0 = match (cb.0)().cast::<()>().0 { Ok(v) => v, Err(err) => return Value(Err(err), Default::default()), }; - let v1 = match (cb.1)().into_erased::<()>().0 { + let v1 = match (cb.1)().cast::<()>().0 { Ok(v) => v, Err(err) => return Value(Err(err), Default::default()), }; - let v2 = match (cb.2)().into_erased::<()>().0 { + let v2 = match (cb.2)().cast::<()>().0 { Ok(v) => v, Err(err) => return Value(Err(err), Default::default()), }; @@ -17,7 +17,10 @@ pub mod symbol; mod transform; mod walk; +use core::ops::ControlFlow; + pub use build::*; +use effect::ConvertShort; pub use transform::*; pub use walk::*; @@ -55,7 +58,11 @@ pub const TAG_ENUM: Symbol = Symbol::new("Enum"); pub enum DefaultMode {} -pub type Status = Result<(), ()>; +#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum Status { + Ok, + Err, +} #[derive(Clone, Copy, PartialEq, Debug)] #[must_use] @@ -67,7 +74,23 @@ pub enum Flow { Done, } +impl ConvertShort<ControlFlow<Flow>> for Option<never::Never> { + fn convert_short(short: Self) -> ControlFlow<Flow> { + match short { + None => ControlFlow::Break(Flow::Done), + Some(_) => unreachable!(), + } + } +} + impl Flow { + pub fn to_status(self) -> Status { + match self { + Flow::Continue | Flow::Done => Status::Ok, + Flow::Err => Status::Err, + } + } + pub fn to_done(self) -> Self { match self { Flow::Continue | Flow::Done => Flow::Done, @@ -81,6 +104,13 @@ impl Flow { Flow::Err => Flow::Err, } } + + pub fn to_control_flow(self) -> ControlFlow<Self> { + match self { + Flow::Continue => ControlFlow::Continue(()), + flow => ControlFlow::Break(flow), + } + } } #[macro_export] @@ -137,7 +167,7 @@ macro_rules! Walk { $(pub const $field: usize = Fields::$field as usize;)* } - use $crate::effect::Effective; + use $crate::effect::EffectiveExt; match index { $(fields::$field => { @@ -154,7 +184,7 @@ macro_rules! Walk { Err(err) => { Err(FieldError(FieldErrorKind::$field(err))) } - }).into_erased() + }) // E::map($crate::Walker::<'ctx, E>::walk(walker, visitor), |result| match result { // Ok(_) => { @@ -165,7 +195,7 @@ macro_rules! Walk { // } // }) })* - _ => E::ready(Ok($crate::Flow::Done)).into_erased() + _ => E::ready(Ok($crate::Flow::Done)) } } } @@ -211,3 +241,45 @@ macro_rules! Walk { // todo!(); // } // } + + +pub mod demo { + use crate::Walk; + use macro_rules_attribute::derive; + use crate::{ + effect::{ + blocking::{BlockOn, Blocking, Spin}, + // r#async::Async, + Effective as _, + }, + transform, Build, DefaultMode, + }; + + #[derive(Walk!, Debug)] + pub struct X { + pub a: bool, + pub b: bool, + pub c: bool, + } + + #[derive(Build!, Debug, PartialEq)] + pub struct Y { + pub a: bool, + pub b: bool, + pub c: bool, + } + + #[inline(never)] + pub fn ident(x: X) -> Y { + let other = + transform::<<Y as crate::Build<'_, DefaultMode, _>>::Builder, _, Blocking<Spin>>( + ((), (), ()), + Walk::<DefaultMode, _>::into_walker(&x), + ) + .value(); + + // let other = Spin::block_on(other.into_future()); + + other.0.unwrap() + } +} diff --git a/src/macros/build.rs b/src/macros/build.rs index 380063b..b990f08 100644 --- a/src/macros/build.rs +++ b/src/macros/build.rs @@ -48,7 +48,7 @@ macro_rules! Build { fn new_builders<'a>(seed: Self::Seed) -> $crate::effect::ErasedEffective<'a, Self::Builders, E> { let ($($field),*) = seed; - use $crate::effect::Effective; + use $crate::effect::EffectiveExt; $crate::effect::join( ($(<<$type as $crate::Build<'ctx, M, E>>::Builder as $crate::Builder::<E>>::from_seed($field),)*) @@ -56,7 +56,7 @@ macro_rules! Build { Builders { $($field),* } - }).into_erased() + }) // E::wrap(async move { // Builders { @@ -67,7 +67,7 @@ macro_rules! Build { fn from_builders<'a>(builders: Self::Builders) -> $crate::effect::ErasedEffective<'a, Result<Self::T, Self::Error>, E> { use $crate::Builder; - use $crate::effect::Effective; + use $crate::effect::EffectiveExt; $crate::effect::try_join( ( @@ -78,7 +78,7 @@ macro_rules! Build { $($field),* }), Err(err) => Err(err) - }).into_erased() + }) // E::wrap(async { // Ok($name { diff --git a/src/protocol/visitor.rs b/src/protocol/visitor.rs index fbcfdd2..6dc58fb 100644 --- a/src/protocol/visitor.rs +++ b/src/protocol/visitor.rs @@ -1,4 +1,10 @@ -use crate::Flow; +use core::ops::ControlFlow; + +use crate::{ + effect::{Effective, EffectiveExt as _, ErasedEffective, Ss}, + never::Never, + Flow, Status, +}; mod recoverable; mod request_hint; @@ -25,6 +31,122 @@ pub enum VisitResult<S> { Control(Flow), } +impl<S> VisitResult<S> { + pub fn unit_skipped(self) -> VisitResult<()> { + match self { + VisitResult::Skipped(_) => VisitResult::Skipped(()), + VisitResult::Control(flow) => VisitResult::Control(flow), + } + } + + pub fn to_status(self) -> Status { + match self { + VisitResult::Skipped(_) => Status::Ok, + VisitResult::Control(flow) => flow.to_status(), + } + } + + pub fn to_flow(self) -> Option<Flow> { + match self { + VisitResult::Skipped(_) => None, + VisitResult::Control(flow) => Some(flow), + } + } + + pub fn dont_break_if_skipped(self) -> Option<Status> { + match self { + VisitResult::Skipped(_) => None, + VisitResult::Control(Flow::Continue) => Some(Status::Ok), + VisitResult::Control(Flow::Done) => Some(Status::Ok), + VisitResult::Control(Flow::Err) => Some(Status::Err), + } + } + + pub fn break_if_done_or_err(self) -> Option<Status> { + match self { + VisitResult::Skipped(_) => None, + VisitResult::Control(Flow::Continue) => None, + VisitResult::Control(Flow::Done) => Some(Status::Ok), + VisitResult::Control(Flow::Err) => Some(Status::Err), + } + } + + pub fn break_if_err(self) -> Option<Status> { + match self { + VisitResult::Skipped(_) => None, + VisitResult::Control(Flow::Continue) => None, + VisitResult::Control(Flow::Done) => None, + VisitResult::Control(Flow::Err) => Some(Status::Err), + } + } +} + +pub trait EffectiveVisitExt<'lt>: Effective<'lt> { + fn if_skipped<'ctx: 'lt, 'wrap, Ctx: Ss, F: Ss>( + self, + f: F, + ) -> ErasedEffective<'wrap, (Ctx, VisitResult<()>), Self::Effect> + where + Self: Effective<'lt, Output = (Ctx, VisitResult<()>)>, + F: for<'temp> FnOnce( + &'temp mut Ctx, + ) + -> ErasedEffective<'temp, VisitResult<()>, Self::Effect, &'ctx ()>, + 'lt: 'wrap, + { + self.r#do( + |(ctx, v)| { + ( + ctx, + match v { + VisitResult::Skipped(()) => ControlFlow::Continue(()), + v => ControlFlow::Break(v), + }, + ) + }, + |ctx, ()| f(ctx).cast(), + |_, v| ControlFlow::<_, Never>::Break(v), + |_, _| unreachable!(), + |_, _, _: Never| unreachable!(), + |ctx, _, v| (ctx, v), + ) + } + + fn if_not_finished<'ctx: 'lt, 'wrap, Ctx: Ss, F: Ss>( + self, + f: F, + ) -> ErasedEffective<'wrap, (Ctx, VisitResult<()>), Self::Effect> + where + Self: Effective<'lt, Output = (Ctx, VisitResult<()>)>, + F: for<'temp> FnOnce( + &'temp mut Ctx, + ) + -> ErasedEffective<'temp, VisitResult<()>, Self::Effect, &'ctx ()>, + 'lt: 'wrap, + { + self.r#do( + |(ctx, v)| { + ( + ctx, + match v { + VisitResult::Skipped(()) | VisitResult::Control(Flow::Continue) => { + ControlFlow::Continue(()) + } + v => ControlFlow::Break(v), + }, + ) + }, + |ctx, ()| f(ctx).cast(), + |_, v| ControlFlow::<_, Never>::Break(v), + |_, _| unreachable!(), + |_, _, _: Never| unreachable!(), + |ctx, _, v| (ctx, v), + ) + } +} + +impl<'lt, T: Effective<'lt>> EffectiveVisitExt<'lt> for T {} + impl<S> PartialEq for VisitResult<S> { fn eq(&self, other: &Self) -> bool { match (self, other) { diff --git a/src/protocol/visitor/recoverable.rs b/src/protocol/visitor/recoverable.rs index 7a2b455..9f67c22 100644 --- a/src/protocol/visitor/recoverable.rs +++ b/src/protocol/visitor/recoverable.rs @@ -1,6 +1,6 @@ use crate::{ any::TypeName, - effect::{Effect, Effective, ErasedEffective}, + effect::{Effect, Effective, ErasedEffective, ReadyExt as _}, higher_ranked_type, hkt::Marker, protocol::{ @@ -76,6 +76,6 @@ pub fn visit_recoverable<'a, 'ctx, E: Effect>( object.visit(scope) } else { // If the visitor doesn't support request hint then we continue. - E::ready(VisitResult::Skipped(scope)).into_erased() + VisitResult::Skipped(scope).ready() } } diff --git a/src/protocol/visitor/request_hint.rs b/src/protocol/visitor/request_hint.rs index f60005e..73c95db 100644 --- a/src/protocol/visitor/request_hint.rs +++ b/src/protocol/visitor/request_hint.rs @@ -1,6 +1,6 @@ use crate::{ any::TypeName, - effect::{Effect, Effective, ErasedEffective}, + effect::{Effect, Effective, ErasedEffective, ReadyExt as _}, higher_ranked_type, hkt::Marker, protocol::{DynVisitor, DynWalker}, @@ -54,6 +54,6 @@ pub fn visit_request_hint<'a, 'ctx, E: Effect>( object.request_hint(walker) } else { // If the visitor doesn't support request hint then we continue. - E::ready(VisitResult::Skipped(walker)).into_erased() + VisitResult::Skipped(walker).ready() } } diff --git a/src/protocol/visitor/sequence.rs b/src/protocol/visitor/sequence.rs index 4f18f49..e748eef 100644 --- a/src/protocol/visitor/sequence.rs +++ b/src/protocol/visitor/sequence.rs @@ -1,6 +1,6 @@ use crate::{ any::TypeName, - effect::{Effect, Effective, ErasedEffective}, + effect::{Effect, Effective, ErasedEffective, ReadyExt as _}, higher_ranked_type, hkt::Marker, protocol::{ @@ -17,10 +17,12 @@ use super::VisitResult; /// This protocol uses a scope to give temporary control to the visitor. /// The visitor will drive the walker for each item. pub trait Sequence<'ctx, E: Effect> { - fn visit<'a>( + fn visit<'a: 'c, 'b: 'c, 'c>( &'a mut self, - scope: DynSequenceScope<'a, 'ctx, E>, - ) -> ErasedEffective<'a, VisitResult<DynSequenceScope<'a, 'ctx, E>>, E>; + scope: DynSequenceScope<'b, 'ctx, E>, + ) -> ErasedEffective<'c, VisitResult<DynSequenceScope<'b, 'ctx, E>>, E> + where + 'ctx: 'a; } pub struct SequenceProto<E: Effect>(Marker<E>); @@ -44,7 +46,12 @@ higher_ranked_type! { pub trait SequenceScope<'ctx, E: Effect> { fn size_hint(&mut self) -> ErasedEffective<'_, (usize, Option<usize>), E>; - fn next<'a>(&'a mut self, visitor: DynVisitor<'a, 'ctx>) -> ErasedEffective<'a, Flow, E>; + fn next<'a: 'c, 'b: 'c, 'c>( + &'a mut self, + visitor: DynVisitor<'b, 'ctx>, + ) -> ErasedEffective<'c, Flow, E> + where + 'ctx: 'c + 'a + 'b; } pub type DynSequenceScope<'a, 'ctx, E> = &'a mut (dyn SequenceScope<'ctx, E> + Send + Sync + 'a); @@ -96,6 +103,6 @@ pub fn visit_sequence<'a, 'ctx, E: Effect>( object.visit(scope) } else { // If the visitor doesn't support request hint then we continue. - E::ready(VisitResult::Skipped(scope)).into_erased() + VisitResult::Skipped(scope).ready() } } diff --git a/src/protocol/visitor/tag.rs b/src/protocol/visitor/tag.rs index 5053bd0..33b76ec 100644 --- a/src/protocol/visitor/tag.rs +++ b/src/protocol/visitor/tag.rs @@ -1,6 +1,8 @@ +use core::any::TypeId; + use crate::{ any::TypeName, - effect::{all_ctx, merge_ctx, Effect, Effective, ErasedEffective}, + effect::{Effect, Effective, EffectiveExt, ErasedEffective, ReadyExt}, higher_ranked_type, hkt::Marker, protocol::{ @@ -8,7 +10,7 @@ use crate::{ DynVisitor, }, symbol::Symbol, - DynWalkerAdapter, DynWalkerError, DynWalkerObjSafe, Flow, WalkerTypes, + tri, DynWalkerAdapter, DynWalkerError, DynWalkerObjSafe, WalkerTypes, }; use super::VisitResult; @@ -118,7 +120,7 @@ impl<K: TagKind, E: Effect> HintMeta for TagProto<K, E> { pub enum TagErrorKind<E> { NeverWalked, - /// This can only happen if a panic happens furing the walk and is then caught before calling + /// This can only happen if a panic happens during the walk and is then caught before calling /// finish. WalkNeverFinished, @@ -165,91 +167,58 @@ impl<E> TagError<E> { } #[inline(always)] -pub fn visit_tag<'a, 'ctx: 'a, K: TagKind, E: Effect, W: crate::Walker<'ctx, E> + 'a>( +pub fn visit_tag< + 'ctx: 'visitor, + 'visitor: 'wrap, + 'wrap, + K: TagKind, + E: Effect, + W: crate::Walker<'ctx, E> + 'wrap, +>( kind: K, - visitor: DynVisitor<'a, 'ctx>, + visitor: DynVisitor<'visitor, 'ctx>, walker: W, -) -> ErasedEffective<'a, Result<VisitResult<W>, TagError<W::Error>>, E> +) -> ErasedEffective<'wrap, Result<VisitResult<W>, TagError<W::Error>>, E> where W: WalkerTypes, { // Wrap the walker to allow it to be passed to a dyn walker argument. let walker = DynWalkerAdapter::new(walker); + // Give everything to the effective chain to be used as context. E::ready((kind, visitor, walker)) - .as_ctx( - all_ctx, - |(kind, visitor, walker), _| { - let object = visitor.upcast_mut::<TagProto<K, E>>(); - - E::ready((*kind, object, walker)) - .as_ctx( - all_ctx, - |(kind, object, walker), _| match object { - Some(object) => object - .visit(*kind, &mut **walker) - .map(|value| match value { - VisitResult::Skipped(_) => None, - VisitResult::Control(flow) => Some(flow), - }) - .into_erased(), - None => E::ready(None).into_erased(), - }, - merge_ctx, - ) - .map(|((_, _, _), value)| value) - .into_erased() - }, - merge_ctx, - ) - .as_ctx( - all_ctx, - |((kind, visitor, walker), result), _| { - if result.is_some() - || core::any::TypeId::of::<TagDyn>() != core::any::TypeId::of::<K>() - { - E::ready(None).into_erased() - } else { - let object = visitor.upcast_mut::<TagProto<TagDyn, E>>(); - - E::ready((*kind, object, walker)) - .as_ctx( - all_ctx, - |(kind, object, walker), _| match object { - Some(object) => object - .visit(TagDyn(kind.symbol()), &mut **walker) - .map(|value| match value { - VisitResult::Skipped(_) => None, - VisitResult::Control(flow) => Some(flow), - }) - .into_erased(), - None => E::ready(None).into_erased(), - }, - merge_ctx, - ) - .map(|((_, _, _), value)| value) - .into_erased() - } - }, - merge_ctx, - ) - .map(|((ctx, result), other)| match (result, other) { - (Some(result), None) => (ctx, Some(result)), - (None, Some(result)) => (ctx, Some(result)), - (Some(_), Some(_)) => unreachable!(), - (None, None) => (ctx, None), + .as_ctx(|(kind, visitor, walker)| { + // Try to visit the tag kind as given. + tri!(visitor.upcast_mut::<TagProto<K, E>>()) + .visit(*kind, walker) + .map(VisitResult::to_flow) + .cast() + }) + .as_ctx_or_else(|(kind, visitor, walker)| { + // If the tag type is already dynamic we don't need to try visiting again. + if TypeId::of::<K>() == TypeId::of::<TagDyn>() { + return None.ready(); + } + + // Visit using the dynamic tag, but with the same symbol. + tri!(visitor.upcast_mut::<TagProto<TagDyn, E>>()) + .visit(TagDyn(kind.symbol()), walker) + .map(VisitResult::to_flow) + .cast() }) .map(|((kind, _, walker), result)| match result { + // The visit was performed so return the control flow. Some(flow) => Ok(VisitResult::Control(flow)), + + // The visit was not performed. + // We need to give back the walker as we got it. None => match walker.into_inner() { Ok(walker) => Ok(VisitResult::Skipped(walker)), Err(err) => Err(map_walker_err(kind, err)), }, }) - .into_erased() } -#[inline(always)] fn map_walker_err<K: TagKind, W: WalkerTypes>( kind: K, err: DynWalkerError<W>, diff --git a/src/protocol/visitor/value.rs b/src/protocol/visitor/value.rs index 11c78ff..2c616ec 100644 --- a/src/protocol/visitor/value.rs +++ b/src/protocol/visitor/value.rs @@ -4,7 +4,7 @@ use crate::{ any::TypeName, - effect::{Effect, Effective, ErasedEffective}, + effect::{Effect, Effective, ErasedEffective, ReadyExt as _}, higher_ranked_type, hkt::Marker, protocol::{ @@ -108,6 +108,6 @@ where object.visit(value) } else { // If the visitor doesn't support request hint then we continue. - E::ready(VisitResult::Skipped(value)).into_erased() + VisitResult::Skipped(value).ready() } } diff --git a/src/transform.rs b/src/transform.rs index 65b9887..caf24ab 100644 --- a/src/transform.rs +++ b/src/transform.rs @@ -2,7 +2,7 @@ use core::marker::PhantomData; use crate::{ build::Builder, - effect::{all_ctx, merge_ctx, Effect, Effective, EffectiveExt, ErasedEffective}, + effect::{Effect, Effective, EffectiveExt, ErasedEffective}, hkt::Marker, BuilderTypes, Walk, Walker, WalkerTypes, }; @@ -14,17 +14,12 @@ pub fn transform<'a, 'ctx: 'a, B: Builder<'ctx, E> + 'a, W: Walker<'ctx, E> + 'a walker: W, ) -> ErasedEffective<'a, (Result<B::Value, B::Error>, Result<W::Output, W::Error>), E> { B::from_seed(seed) - .as_ctx( - all_ctx, - |builder, _| walker.walk(builder.as_visitor()).into_erased(), - merge_ctx, - ) + .as_ctx(|builder| walker.walk(builder.as_visitor()).cast()) .then(|(builder, walker_result)| { builder .build() .map(|builder_result| (builder_result, walker_result)) }) - .into_erased() // B::from_seed(seed).map_with(walker, |builder, walker| walker.walk(builder.as_visitor())); // E::wrap(async { diff --git a/src/walk.rs b/src/walk.rs index 9f4a828..9c6cd10 100644 --- a/src/walk.rs +++ b/src/walk.rs @@ -1,7 +1,7 @@ pub mod walkers; use crate::{ - effect::{all_ctx, merge_ctx, Effect, Effective, ErasedEffective}, + effect::{Effect, Effective, EffectiveExt, ErasedEffective}, protocol::DynVisitor, Flow, }; @@ -121,34 +121,29 @@ impl<'ctx, W: Walker<'ctx, E>, E: Effect> WalkerObjSafe<'ctx, E> for DynWalkerAd core::mem::replace(&mut self.state, DynWalkerState::Walking) { E::ready((self, visitor)) - .as_ctx( - all_ctx, - |(this, visitor), _| { - // Walk the walker. - walker - .walk(visitor.cast()) - .map(|value| match value { - Ok(value) => { - this.state = DynWalkerState::Done(value); - Flow::Done - } - Err(err) => { - this.state = DynWalkerState::Err(err); - - // Signal that control flow should stop as soon as possible as we - // are in an error state. - Flow::Err - } - }) - .into_erased() - }, - merge_ctx, - ) + .as_ctx(|(this, visitor)| { + // Walk the walker. + walker + .walk(visitor.cast()) + .map(|value| match value { + Ok(value) => { + this.state = DynWalkerState::Done(value); + Flow::Done + } + Err(err) => { + this.state = DynWalkerState::Err(err); + + // Signal that control flow should stop as soon as possible as we + // are in an error state. + Flow::Err + } + }) + .cast() + }) .map(|(_, value)| value) - .into_erased() } else { // Can't do anything if the walker has already been walked. - E::ready(Flow::Done).into_erased() + E::ready(Flow::Done) } } } diff --git a/src/walk/walkers/core/key_value.rs b/src/walk/walkers/core/key_value.rs index 3c0f700..1adb7b1 100644 --- a/src/walk/walkers/core/key_value.rs +++ b/src/walk/walkers/core/key_value.rs @@ -1,16 +1,20 @@ use core::marker::PhantomData; use crate::{ - effect::{all_ctx, merge_ctx, Effect, Effective, ErasedEffective}, + effect::{Effect, EffectExt as _, Effective, EffectiveExt as _, ErasedEffective}, never::Never, protocol::{ - visitor::{visit_tag, TagConst, TagError, TagKind, VisitResult}, + visitor::{ + tags, visit_tag, EffectiveVisitExt as _, TagConst, TagError, TagKind, VisitResult, + }, DynVisitor, }, walkers::core::noop::NoopWalker, Flow, WalkerTypes, TAG_KEY, TAG_KEY_VALUE, TAG_VALUE, }; +use super::value::ValueWalker; + pub struct KeyValueWalker<T, K, V> { key_walker: K, value_walker: V, @@ -53,7 +57,7 @@ impl<'ctx, T, K, V, E> crate::Walker<'ctx, E> for KeyValueWalker<T, K, V> where E: Effect, T: TagKind, - K: crate::Walker<'ctx, E>, + K: crate::Walker<'ctx, E> + 'ctx, V: crate::Walker<'ctx, E> + 'ctx, { #[inline(always)] @@ -61,22 +65,38 @@ where self, visitor: DynVisitor<'b, 'ctx>, ) -> ErasedEffective<'c, Result<Self::Output, Self::Error>, E> { - E::ready(visitor) - .as_ctx( - all_ctx, - move |visitor, _| { - self.value_walker - .walk(visitor.cast()) - .map(|result| match result { - Ok(_) => Ok::<_, Self::Error>(()), - Err(_err) => todo!(), - }) - .into_erased() - }, - merge_ctx, - ) - .map(|(_, value)| value) - .into_erased() + let Self { + key_walker, + value_walker, + tag, + } = self; + + E::as_ctx( + visitor, + #[inline(always)] + |visitor| { + visit_tag::<tags::Key, E, _>(TagConst, visitor.cast(), key_walker) + .map(|result| match result { + Ok(visit) => visit.unit_skipped(), + Err(_) => Flow::Err.into(), + }) + .cast() + }, + ) + .if_not_finished( + #[inline(always)] + |visitor| { + value_walker + .walk(visitor.cast()) + .map(|result| match result { + Ok(_) => Flow::Done.into(), + Err(_err) => Flow::Err.into(), + }) + .cast() + }, + ) + .remove_ctx() + .map(|visit| Ok(())) // E::wrap(async move { // match visit_tag::<T, E, _>(self.tag, visitor.cast(), NoopWalker::new()).await { diff --git a/src/walk/walkers/core/noop.rs b/src/walk/walkers/core/noop.rs index 6c846e7..d961d7e 100644 --- a/src/walk/walkers/core/noop.rs +++ b/src/walk/walkers/core/noop.rs @@ -29,6 +29,6 @@ impl<'ctx, E: Effect> crate::Walker<'ctx, E> for NoopWalker { self, _visitor: DynVisitor<'b, 'ctx>, ) -> ErasedEffective<'c, Result<Self::Output, Self::Error>, E> { - E::ready(Ok(())).into_erased() + E::ready(Ok(())) } } diff --git a/src/walk/walkers/core/struct.rs b/src/walk/walkers/core/struct.rs index 82e399d..393450f 100644 --- a/src/walk/walkers/core/struct.rs +++ b/src/walk/walkers/core/struct.rs @@ -3,15 +3,15 @@ use core::{any::TypeId, marker::PhantomData}; use crate::{ any::{AnyTrait, BorrowedStatic, BorrowedStaticHrt}, any_trait, - effect::{all_ctx, merge_ctx, Effect, Effective, EffectiveExt, ErasedEffective}, + effect::{Effect, EffectExt as _, Effective, EffectiveExt, ErasedEffective, ReadyExt as _}, hkt::Marker, never::Never, protocol::{ visitor::{ visit_recoverable, visit_request_hint, visit_sequence, visit_tag, visit_value, - RecoverableKnown, RecoverableProto, RecoverableScope, SequenceKnown, SequenceProto, - SequenceScope, TagConst, TagDyn, TagError, TagHint, TagKnown, TagProto, ValueKnown, - ValueProto, VisitResult, + EffectiveVisitExt as _, RecoverableKnown, RecoverableProto, RecoverableScope, + SequenceKnown, SequenceProto, SequenceScope, TagConst, TagDyn, TagError, TagHint, + TagKnown, TagProto, ValueKnown, ValueProto, VisitResult, }, walker::hint::{Hint, HintMeta, HintProto, MetaHint, MetaKnown}, DynVisitor, DynWalker, @@ -104,6 +104,16 @@ where _generics: Default::default(), } } + + fn record_tag_error<U>( + &mut self, + result: Result<VisitResult<U>, TagError<Never>>, + ) -> VisitResult<U> { + result.unwrap_or_else(|err| { + self.error = Some(StructWalkErrorKind::Tag(err)); + Flow::Err.into() + }) + } } impl<'ctx, I, S, M, E> WalkerTypes for StructWalker<'ctx, I, S, M, E> @@ -130,19 +140,15 @@ where { E::ready((self, visitor)) .as_ctx( - all_ctx, #[inline(always)] - |(this, visitor), _| { - RecoverableScope::<'ctx, E>::new_walk::<'_, '_, '_>(this, visitor.cast()) - .into_erased() + |(this, visitor)| { + RecoverableScope::<'ctx, E>::new_walk::<'_, '_, '_>(this, visitor.cast()).cast() }, - merge_ctx, ) .map(|((this, _), _)| match this.error { Some(err) => Err(StructWalkError { kind: err }), None => Ok(()), }) - .into_erased() // E::ready(self).as_ctx_for::<'ctx, '_>(|this, _| { // (RecoverableScope::<'ctx, E>::new_walk::<'_, 'b, '_>(this, visitor), PhantomData) @@ -175,6 +181,7 @@ any_trait! { ] where E: Effect, I: StructTypeInfo<'ctx, M, S = StaticType>, + M: 'ctx, I::T: 'static } @@ -205,7 +212,7 @@ where &'a mut self, _hint: &'a <RecoverableProto<E> as HintMeta>::Hint, ) -> ErasedEffective<'a, Result<MetaKnown<'a, 'ctx, RecoverableProto<E>>, ()>, E> { - E::ready(Ok(RecoverableKnown)).into_erased() + E::ready(Ok(RecoverableKnown)) } } @@ -251,7 +258,6 @@ where E::ready(Ok(TagKnown { kind_available: Some(true), })) - .into_erased() } } @@ -297,7 +303,6 @@ where E::ready(Ok(TagKnown { kind_available: Some(true), })) - .into_erased() } } @@ -339,7 +344,6 @@ where E::ready(Ok(TagKnown { kind_available: Some(true), })) - .into_erased() } } @@ -385,7 +389,6 @@ where E::ready(Ok(TagKnown { kind_available: Some(true), })) - .into_erased() } } @@ -432,7 +435,6 @@ where E::ready(Ok(TagKnown { kind_available: Some(true), })) - .into_erased() } } @@ -479,7 +481,7 @@ where 'ctx, TagProto<TagConst<{ TAG_FIELD_NAMES.to_int() }>, E>, >::hint(self, visitor, TagHint { kind: TagConst }), - _ => E::ready(Flow::Continue).into_erased(), + _ => E::ready(Flow::Continue), } } @@ -496,7 +498,6 @@ where kind_available: Some(false), }), }) - .into_erased() } } @@ -528,7 +529,7 @@ where &'a mut self, _hint: &'a (), ) -> ErasedEffective<'a, Result<ValueKnown<'a, BorrowedStatic<'ctx, I::T>>, ()>, E> { - E::ready(Ok(ValueKnown { preview: None })).into_erased() + E::ready(Ok(ValueKnown { preview: None })) } } @@ -560,11 +561,10 @@ where E::ready(Ok(SequenceKnown { len: (len, Some(len)), })) - .into_erased() } } -impl<'ctx, I, S, M, E> SequenceScope<'ctx, E> for StructWalker<'ctx, I, S, M, E> +impl<'ctx, I, S, M: 'ctx, E> SequenceScope<'ctx, E> for StructWalker<'ctx, I, S, M, E> where E: Effect, I: StructTypeInfo<'ctx, M, S = S>, @@ -573,28 +573,29 @@ where fn size_hint(&mut self) -> ErasedEffective<'_, (usize, Option<usize>), E> { let len = I::FIELDS.len(); - E::ready((len, Some(len))).into_erased() + E::ready((len, Some(len))) } #[inline(always)] - fn next<'a>(&'a mut self, visitor: DynVisitor<'a, 'ctx>) -> ErasedEffective<'a, Flow, E> { + fn next<'a: 'c, 'b: 'c, 'c>( + &'a mut self, + visitor: DynVisitor<'b, 'ctx>, + ) -> ErasedEffective<'c, Flow, E> { if self.index >= I::FIELDS.len() { - return E::ready(Flow::Done).into_erased(); + return Flow::Done.ready(); } let index = self.index; self.index += 1; - I::walk_field::<E>(index, self.value, visitor) - .map(|result| match result { - Ok(flow) => flow, - Err(err) => { - // Record the error and signal a break. - self.error = Some(StructWalkErrorKind::Field(err)); - Flow::Err - } - }) - .into_erased() + I::walk_field::<E>(index, self.value, visitor).map(|result| match result { + Ok(flow) => flow, + Err(err) => { + // Record the error and signal a break. + self.error = Some(StructWalkErrorKind::Field(err)); + Flow::Err + } + }) // E::map( // I::walk_field::<E>(index, self.value, visitor), @@ -610,7 +611,7 @@ where } } -impl<'ctx, I, M, E> RecoverableScope<'ctx, E> for StructWalker<'ctx, I, StaticType, M, E> +impl<'ctx, I, M: 'ctx, E> RecoverableScope<'ctx, E> for StructWalker<'ctx, I, StaticType, M, E> where E: Effect, I: StructTypeInfo<'ctx, M, S = StaticType>, @@ -619,7 +620,7 @@ where #[inline(always)] fn new_walk<'a: 'c, 'b: 'c, 'c>( &'a mut self, - mut visitor: DynVisitor<'b, 'ctx>, + visitor: DynVisitor<'b, 'ctx>, ) -> ErasedEffective<'c, Status, E> { // Reset the errors to default state. self.error = None; @@ -627,225 +628,49 @@ where // Reset the field index to the default. self.index = 0; - E::ready((self, visitor)) - .as_ctx( - all_ctx, - |(this, visitor), _| { - visit_request_hint::<E>(visitor.cast(), DynWalker(*this)) - .map(|result| match result { - VisitResult::Control(Flow::Continue) | VisitResult::Skipped(_) => None, - VisitResult::Control(Flow::Done) => Some(Ok(())), - VisitResult::Control(Flow::Err) => Some(Err(())), - }) - .into_erased() - }, - merge_ctx, + E::as_ctx((self, visitor), |(this, visitor)| { + visit_request_hint::<E>(visitor.cast(), DynWalker(*this)) + .map(VisitResult::unit_skipped) + .cast() + }) + .if_not_finished(|(this, visitor)| { + visit_value::<_, E>(visitor.cast(), BorrowedStatic(this.value)) + .map(VisitResult::unit_skipped) + .cast() + }) + .if_not_finished(|(this, visitor)| { + visit_tag::<TagConst<{ TAG_TYPE_ID.to_int() }>, E, _>( + TagConst, + visitor.cast(), + ValueWalker::new(TypeId::of::<I::T>()), ) - .as_ctx( - all_ctx, - |((this, visitor), result), _| { - if result.is_some() { - E::ready(None).into_erased() - } else { - visit_sequence::<E>(visitor.cast(), *this) - .map(|result| match result { - VisitResult::Control(Flow::Continue) | VisitResult::Skipped(_) => { - None - } - VisitResult::Control(Flow::Done) => Some(Ok(())), - VisitResult::Control(Flow::Err) => Some(Err(())), - }) - .into_erased() - } - }, - merge_ctx, + .map(|result| this.record_tag_error(result).unit_skipped()) + .cast() + }) + .if_not_finished(|(this, visitor)| { + visit_tag::<TagConst<{ TAG_STRUCT.to_int() }>, E, _>( + TagConst, + visitor.cast(), + NoopWalker::new(), ) - .map(|((ctx, result), other)| match (result, other) { - (Some(result), None) => (ctx, Some(result)), - (None, Some(result)) => (ctx, Some(result)), - (Some(_), Some(_)) => unreachable!(), - (None, None) => (ctx, None), - }) - .as_ctx_or_else(|(this, visitor)| { - // .as_ctx(all_ctx, |((this, visitor), result), _| { - // if result.is_some() { - // E::ready(None).into_erased() - // } else { - visit_value::<_, E>(visitor.cast(), BorrowedStatic(this.value)) - .map(|result| match result { - VisitResult::Control(Flow::Continue) | VisitResult::Skipped(_) => None, - VisitResult::Control(Flow::Done) => Some(Ok(())), - VisitResult::Control(Flow::Err) => Some(Err(())), - }) - .into_erased() - // } - // }) - // .map(|((ctx, result), other)| match (result, other) { - // (Some(result), None) => (ctx, Some(result)), - // (None, Some(result)) => (ctx, Some(result)), - // (Some(_), Some(_)) => unreachable!(), - // (None, None) => (ctx, None), - }) - .as_ctx_or_else(|(this, visitor)| { - visit_tag::<TagConst<{ TAG_TYPE_ID.to_int() }>, E, _>( - TagConst, - visitor.cast(), - ValueWalker::new(TypeId::of::<I::T>()), - ) - .map(|result| match result { - Err(err) => { - this.error = Some(StructWalkErrorKind::Tag(err)); - Some(Err(())) - } - Ok(VisitResult::Control(Flow::Continue) | VisitResult::Skipped(_)) => None, - Ok(VisitResult::Control(Flow::Done)) => Some(Ok(())), - Ok(VisitResult::Control(Flow::Err)) => Some(Err(())), - }) - .into_erased() - }) - // .as_ctx(all_ctx, |((this, visitor), result), _| { - // if result.is_some() { - // E::ready(None).into_erased() - // } else { - // visit_tag::<TagConst<{ TAG_TYPE_ID.to_int() }>, E, _>( - // TagConst, - // visitor.cast(), - // ValueWalker::new(TypeId::of::<I::T>()), - // ) - // .map(|result| match result { - // Err(err) => { - // this.error = Some(StructWalkErrorKind::Tag(err)); - // Some(Err(())) - // } - // Ok(VisitResult::Control(Flow::Continue) | VisitResult::Skipped(_)) => None, - // Ok(VisitResult::Control(Flow::Done)) => Some(Ok(())), - // Ok(VisitResult::Control(Flow::Err)) => Some(Err(())), - // }) - // .into_erased() - // } - // }) - // .map(|((ctx, result), other)| match (result, other) { - // (Some(result), None) => (ctx, Some(result)), - // (None, Some(result)) => (ctx, Some(result)), - // (Some(_), Some(_)) => unreachable!(), - // (None, None) => (ctx, None), - // }) - .map(|(_, value)| match value { - None => Ok(()), - Some(value) => value, - }) - .into_erased() - - // E::wrap(async move { - // // // We should check if the visitor wants something specific. - // // match visit_request_hint::<E>(visitor.cast(), DynWalker(self)).await { - // // VisitResult::Skipped(_) | VisitResult::Control(Flow::Continue) => {} - // // VisitResult::Control(Flow::Done) => return Ok(()), - // // VisitResult::Control(Flow::Err) => return Err(()), - // // } - // // - // // // Attempt to visit the value directly. - // // match visit_value::<_, E>(visitor.cast(), BorrowedStatic(self.value)).await { - // // VisitResult::Skipped(_) | VisitResult::Control(Flow::Continue) => {} - // // VisitResult::Control(Flow::Done) => return Ok(()), - // // VisitResult::Control(Flow::Err) => return Err(()), - // // } - // // - // // // Follow the standard set of protocols for a struct. - // // // - Tagged: type ID - // // // - Tagged: struct - // // // - Tagged: struct name - // // // - Tagged: struct field names - // // // - Sequence: the fields - // // - // // match visit_tag::<TagConst<{ TAG_TYPE_ID.to_int() }>, E, _>( - // // TagConst, - // // visitor.cast(), - // // ValueWalker::new(TypeId::of::<I::T>()), - // // ) - // // .await - // // { - // // Err(err) => { - // // self.error = Some(StructWalkErrorKind::Tag(err)); - // // return Err(()); - // // } - // // Ok(VisitResult::Skipped(_)) | Ok(VisitResult::Control(Flow::Continue)) => {} - // // Ok(VisitResult::Control(Flow::Done)) => return Ok(()), - // // Ok(VisitResult::Control(Flow::Err)) => return Err(()), - // // } - // // - // // match visit_tag::<TagConst<{ TAG_STRUCT.to_int() }>, E, _>( - // // TagConst, - // // visitor.cast(), - // // NoopWalker::new(), - // // ) - // // .await - // // { - // // Ok(VisitResult::Skipped(_)) => { - // // match visit_tag::<TagConst<{ TAG_MAP.to_int() }>, E, _>( - // // TagConst, - // // visitor.cast(), - // // NoopWalker::new(), - // // ) - // // .await - // // { - // // Err(err) => { - // // self.error = Some(StructWalkErrorKind::Tag(err)); - // // return Err(()); - // // } - // // Ok(VisitResult::Skipped(_)) | Ok(VisitResult::Control(Flow::Continue)) => {} - // // Ok(VisitResult::Control(Flow::Done)) => return Ok(()), - // // Ok(VisitResult::Control(Flow::Err)) => return Err(()), - // // } - // // } - // // Err(err) => { - // // self.error = Some(StructWalkErrorKind::Tag(err)); - // // return Err(()); - // // } - // // Ok(VisitResult::Control(Flow::Continue)) => {} - // // Ok(VisitResult::Control(Flow::Done)) => return Ok(()), - // // Ok(VisitResult::Control(Flow::Err)) => return Err(()), - // // } - // // - // // match visit_tag::<TagConst<{ TAG_TYPE_NAME.to_int() }>, E, _>( - // // TagConst, - // // visitor.cast(), - // // ValueWalker::new(I::NAME), - // // ) - // // .await - // // { - // // Err(err) => { - // // self.error = Some(StructWalkErrorKind::Tag(err)); - // // return Err(()); - // // } - // // Ok(VisitResult::Skipped(_)) | Ok(VisitResult::Control(Flow::Continue)) => {} - // // Ok(VisitResult::Control(Flow::Done)) => return Ok(()), - // // Ok(VisitResult::Control(Flow::Err)) => return Err(()), - // // } - // // - // // match visit_tag::<TagConst<{ TAG_FIELD_NAMES.to_int() }>, E, _>( - // // TagConst, - // // visitor.cast(), - // // StaticSliceWalker::<_, ValueWalker<&'static str>>::new(I::FIELDS), - // // ) - // // .await - // // { - // // Err(err) => { - // // self.error = Some(StructWalkErrorKind::FieldTag(err)); - // // return Err(()); - // // } - // // Ok(VisitResult::Skipped(_)) | Ok(VisitResult::Control(Flow::Continue)) => {} - // // Ok(VisitResult::Control(Flow::Done)) => return Ok(()), - // // Ok(VisitResult::Control(Flow::Err)) => return Err(()), - // // } - // - // match visit_sequence::<E>(visitor, self).await { - // VisitResult::Control(Flow::Continue) | VisitResult::Skipped(_) => {} - // VisitResult::Control(Flow::Done) => return Ok(()), - // VisitResult::Control(Flow::Err) => return Err(()), - // } - // - // Ok(()) - // }) + .map(|result| this.record_tag_error(result).unit_skipped()) + .cast() + }) + .if_skipped(|(this, visitor)| { + visit_tag::<TagConst<{ TAG_MAP.to_int() }>, E, _>( + TagConst, + visitor.cast(), + NoopWalker::new(), + ) + .map(|result| this.record_tag_error(result).unit_skipped()) + .cast() + }) + .if_not_finished(|(this, visitor)| { + visit_sequence::<E>(visitor.cast(), *this) + .map(VisitResult::unit_skipped) + .cast() + }) + .remove_ctx() + .map(VisitResult::to_status) } } diff --git a/src/walk/walkers/core/tag.rs b/src/walk/walkers/core/tag.rs index 36d0013..d2b170f 100644 --- a/src/walk/walkers/core/tag.rs +++ b/src/walk/walkers/core/tag.rs @@ -84,7 +84,10 @@ where &'static T: Into<W>, { #[inline(always)] - fn next<'a>(&'a mut self, visitor: DynVisitor<'a, 'ctx>) -> ErasedEffective<'a, Flow, E> { + fn next<'a: 'c, 'b: 'c, 'c>( + &'a mut self, + visitor: DynVisitor<'b, 'ctx>, + ) -> ErasedEffective<'c, Flow, E> { if let Some(name) = self.names.get(self.current) { self.current += 1; todo!() @@ -95,12 +98,12 @@ where // } // }) } else { - E::ready(Flow::Done).into_erased() + E::ready(Flow::Done) } } #[inline(always)] fn size_hint(&mut self) -> ErasedEffective<'_, (usize, Option<usize>), E> { - E::ready((self.names.len(), Some(self.names.len()))).into_erased() + E::ready((self.names.len(), Some(self.names.len()))) } } diff --git a/src/walk/walkers/core/value.rs b/src/walk/walkers/core/value.rs index bc6a44f..72715cb 100644 --- a/src/walk/walkers/core/value.rs +++ b/src/walk/walkers/core/value.rs @@ -1,6 +1,6 @@ use crate::{ any::{BorrowedStatic, OwnedStatic}, - effect::{Effect, Effective, ErasedEffective}, + effect::{Effect, Effective, EffectiveExt as _, ErasedEffective}, never::Never, protocol::{visitor::visit_value, DynVisitor}, WalkerTypes, @@ -48,9 +48,7 @@ impl<'ctx, T: Send + Sync + 'static, E: Effect> crate::Walker<'ctx, E> for Value visitor: DynVisitor<'b, 'ctx>, ) -> ErasedEffective<'c, Result<Self::Output, Self::Error>, E> { // Attempt to visit using the value protocol. - visit_value::<_, E>(visitor, OwnedStatic(self.0)) - .map(|_| Ok(())) - .into_erased() + visit_value::<_, E>(visitor, OwnedStatic(self.0)).map(|_| Ok(())) // E::map( // visit_value::<_, E>(visitor, OwnedStatic(self.0)), // |_| Ok(()), diff --git a/tests/builder_struct.rs b/tests/builder_struct.rs index b3502dd..290676b 100644 --- a/tests/builder_struct.rs +++ b/tests/builder_struct.rs @@ -117,7 +117,6 @@ impl<'ctx, M, E: Effect> builders::core::r#struct::StructTypeInfo<'ctx, M, E> fo b: builders.b.build().into_future().await.unwrap(), }) }) - .into_erased() } #[inline(always)] @@ -141,7 +140,6 @@ impl<'ctx, M, E: Effect> builders::core::r#struct::StructTypeInfo<'ctx, M, E> fo b: Builder::<E>::from_seed(()).into_future().await, } }) - .into_erased() } } @@ -308,23 +306,33 @@ pub mod demo { pub struct X { pub a: bool, pub b: bool, + pub c: bool, } - #[derive(Build!, Debug)] + #[derive(Build!, Debug, PartialEq)] pub struct Y { pub b: bool, pub a: bool, + pub c: bool, } #[no_mangle] + #[inline(never)] pub fn ident(x: X) -> Y { - let other = transform::<<Y as crate::Build<'_, DefaultMode, _>>::Builder, _, Blocking<Spin>>( - ((), ()), - Walk::<DefaultMode, _>::into_walker(&x), - ); + let other = + transform::<<Y as crate::Build<'_, DefaultMode, _>>::Builder, _, Blocking<Spin>>( + ((), (), ()), + Walk::<DefaultMode, _>::into_walker(&x), + ) + .value(); - let other = Spin::block_on(other.into_future()); + // let other = Spin::block_on(other.into_future()); other.0.unwrap() } + + #[test] + fn demo() { + assert_eq!(ident(X { a: true, b: false, c: true }), Y { a: true, b: false, c: true }); + } } diff --git a/tests/common/builder.rs b/tests/common/builder.rs index a40e8fa..35bc8a8 100644 --- a/tests/common/builder.rs +++ b/tests/common/builder.rs @@ -53,14 +53,14 @@ impl<'ctx, Seed: Send + Sync, Value: Send + Sync, Error: Send + Sync, E: Effect> where Self: 'a, { - E::ready(Self::from_seed(seed)).into_erased() + E::ready(Self::from_seed(seed)) } fn build<'a>(self) -> ErasedEffective<'a, Result<Self::Value, Self::Error>, E> where Self: 'a, { - E::ready(self.build()).into_erased() + E::ready(self.build()) } fn as_visitor(&mut self) -> DynVisitor<'_, 'ctx> { diff --git a/tests/common/protocol/hint.rs b/tests/common/protocol/hint.rs index 5809380..ec94277 100644 --- a/tests/common/protocol/hint.rs +++ b/tests/common/protocol/hint.rs @@ -33,13 +33,13 @@ impl<'ctx, P: HintMeta> Hint<'ctx, P> for MockHintWalker<P> { visitor: DynVisitor<'a, 'ctx>, hint: MetaHint<'a, 'ctx, P>, ) -> ErasedEffective<'a, Flow, P::Effect> { - P::Effect::ready(self.hint(visitor, hint)).into_erased() + P::Effect::ready(self.hint(visitor, hint)) } fn known<'a>( &'a mut self, hint: &'a MetaHint<'a, 'ctx, P>, ) -> ErasedEffective<'a, Result<MetaKnown<'a, 'ctx, P>, ()>, P::Effect> { - P::Effect::ready(Self::known(self)(&(), hint)).into_erased() + P::Effect::ready(Self::known(self)(&(), hint)) } } diff --git a/tests/common/protocol/recoverable.rs b/tests/common/protocol/recoverable.rs index 47c6d21..1ccce92 100644 --- a/tests/common/protocol/recoverable.rs +++ b/tests/common/protocol/recoverable.rs @@ -40,7 +40,7 @@ impl<'ctx, E: Effect> Recoverable<'ctx, E> for MockRecoverableVisitor<E> { &'a mut self, scope: DynRecoverableScope<'a, 'ctx, E>, ) -> ErasedEffective<'a, VisitResult<DynRecoverableScope<'a, 'ctx, E>>, E> { - E::ready(self.visit()(&(), scope)).into_erased() + E::ready(self.visit()(&(), scope)) } } @@ -55,6 +55,6 @@ impl<'ctx, E: Effect> RecoverableScope<'ctx, E> for MockRecoverableScopeVisitor< &'a mut self, visitor: DynVisitor<'b, 'ctx>, ) -> ErasedEffective<'c, Status, E> { - E::ready(self.new_walk(visitor)).into_erased() + E::ready(self.new_walk(visitor)) } } diff --git a/tests/common/protocol/request_hint.rs b/tests/common/protocol/request_hint.rs index 012ca99..dc57b5e 100644 --- a/tests/common/protocol/request_hint.rs +++ b/tests/common/protocol/request_hint.rs @@ -28,6 +28,6 @@ impl<'ctx, E: Effect> RequestHint<'ctx, E> for MockRequestHintVisitor<E> { &'a mut self, walker: DynWalker<'a, 'ctx>, ) -> ErasedEffective<'a, VisitResult<DynWalker<'a, 'ctx>>, E> { - E::ready(self.request_hint()(&(), walker)).into_erased() + E::ready(self.request_hint()(&(), walker)) } } diff --git a/tests/common/protocol/sequence.rs b/tests/common/protocol/sequence.rs index 9b3f6b2..c7e68b1 100644 --- a/tests/common/protocol/sequence.rs +++ b/tests/common/protocol/sequence.rs @@ -32,27 +32,30 @@ any_trait! { } impl<'ctx, E: Effect> Sequence<'ctx, E> for MockSequenceVisitor<E> { - fn visit<'a>( + fn visit<'a: 'c, 'b: 'c, 'c>( &'a mut self, - scope: DynSequenceScope<'a, 'ctx, E>, - ) -> ErasedEffective<'a, VisitResult<DynSequenceScope<'a, 'ctx, E>>, E> { - E::ready(self.visit()(&(), scope)).into_erased() + scope: DynSequenceScope<'b, 'ctx, E>, + ) -> ErasedEffective<'c, VisitResult<DynSequenceScope<'b, 'ctx, E>>, E> { + E::ready(self.visit()(&(), scope)) } } mock! { pub SequenceScope<E> { pub fn size_hint(&mut self) -> (usize, Option<usize>); - pub fn next<'a, 'ctx>(&'a mut self, visitor: DynVisitor<'a, 'ctx>) -> Flow; + pub fn next<'a, 'b, 'ctx>(&'a mut self, visitor: DynVisitor<'b, 'ctx>) -> Flow; } } impl<'ctx, E: Effect> SequenceScope<'ctx, E> for MockSequenceScope<E> { fn size_hint(&mut self) -> ErasedEffective<'_, (usize, Option<usize>), E> { - E::ready(self.size_hint()).into_erased() + E::ready(self.size_hint()) } - fn next<'a>(&'a mut self, visitor: DynVisitor<'a, 'ctx>) -> ErasedEffective<'a, Flow, E> { - E::ready(self.next(visitor)).into_erased() + fn next<'a: 'c, 'b: 'c, 'c>( + &'a mut self, + visitor: DynVisitor<'b, 'ctx>, + ) -> ErasedEffective<'c, Flow, E> { + E::ready(self.next(visitor)) } } diff --git a/tests/common/protocol/tag.rs b/tests/common/protocol/tag.rs index 9d2f98a..2dcd805 100644 --- a/tests/common/protocol/tag.rs +++ b/tests/common/protocol/tag.rs @@ -30,6 +30,5 @@ impl<'ctx, K: TagKind, E: Effect> Tag<'ctx, K, E> for MockTagVisitor<K, E> { VisitResult::Skipped(_) => VisitResult::Skipped(walker), VisitResult::Control(flow) => VisitResult::Control(flow), }) - .into_erased() } } diff --git a/tests/common/protocol/visitor.rs b/tests/common/protocol/visitor.rs index fdc9921..b0f2273 100644 --- a/tests/common/protocol/visitor.rs +++ b/tests/common/protocol/visitor.rs @@ -40,6 +40,5 @@ where VisitResult::Skipped(_) => VisitResult::Skipped(value), VisitResult::Control(flow) => VisitResult::Control(flow), }) - .into_erased() } } diff --git a/tests/common/walker.rs b/tests/common/walker.rs index e4e879e..5ba0ef8 100644 --- a/tests/common/walker.rs +++ b/tests/common/walker.rs @@ -31,7 +31,7 @@ impl<'ctx, Output: Send + Sync, Error: Send + Sync, E: Effect> Walker<'ctx, E> where Self: 'c, { - E::ready(self.walk(visitor)).into_erased() + E::ready(self.walk(visitor)) } } diff --git a/tests/protocol_visitor_recoverable.rs b/tests/protocol_visitor_recoverable.rs index a0a0b95..cde5ca1 100644 --- a/tests/protocol_visitor_recoverable.rs +++ b/tests/protocol_visitor_recoverable.rs @@ -6,7 +6,7 @@ use treaty::{ visitor::{Recoverable, ValueProto, VisitResult}, DynVisitor, }, - Flow, + Flow, Status, }; use crate::common::{ @@ -30,10 +30,10 @@ fn recoverable_can_be_visited() { visitor.expect_traits().times(2).return_const(None); // Attempt to walk once. - assert_eq!(scope.new_walk(DynVisitor(&mut visitor)).value(), Ok(())); + assert_eq!(scope.new_walk(DynVisitor(&mut visitor)).value(), Status::Ok); // Attempt to walk twice. - assert_eq!(scope.new_walk(DynVisitor(&mut visitor)).value(), Ok(())); + assert_eq!(scope.new_walk(DynVisitor(&mut visitor)).value(), Status::Ok); // We are done. VisitResult::Control(Flow::Done) @@ -51,7 +51,7 @@ fn recoverable_can_be_visited() { .upcast::<ValueProto<OwnedStatic<i32>, Blocking>>() .is_none()); - Ok(()) + Status::Ok }); // Visit using the recoverable protocol. diff --git a/tests/walker_struct.rs b/tests/walker_struct.rs index 4ff61d1..381b6c1 100644 --- a/tests/walker_struct.rs +++ b/tests/walker_struct.rs @@ -81,7 +81,6 @@ impl<'ctx, M> StructTypeInfo<'ctx, M> for Info { _ => Ok(Flow::Done), } }) - .into_erased() } } |