reviewed the struct builder API
| -rw-r--r-- | Cargo.lock | 1 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | src/any.rs | 4 | ||||
| -rw-r--r-- | src/build.rs | 8 | ||||
| -rw-r--r-- | src/build/builders/core.rs | 20 | ||||
| -rw-r--r-- | src/build/builders/core/bool.rs | 12 | ||||
| -rw-r--r-- | src/build/builders/core/struct.rs | 488 | ||||
| -rw-r--r-- | src/effect.rs | 8 | ||||
| -rw-r--r-- | src/lib.rs | 34 | ||||
| -rw-r--r-- | src/macros/build.rs | 87 | ||||
| -rw-r--r-- | src/protocol.rs | 4 | ||||
| -rw-r--r-- | src/protocol/walker/hint.rs | 42 | ||||
| -rw-r--r-- | src/transform.rs | 78 | ||||
| -rw-r--r-- | src/walk.rs | 4 | ||||
| -rw-r--r-- | src/walk/walkers/core/struct.rs | 178 | ||||
| -rw-r--r-- | tests/builder_struct.rs | 148 | ||||
| -rw-r--r-- | tests/common/builder.rs | 21 | ||||
| -rw-r--r-- | tests/common/protocol.rs | 2 | ||||
| -rw-r--r-- | tests/common/protocol/hint.rs | 15 | ||||
| -rw-r--r-- | tests/common/protocol/value.rs (renamed from tests/common/protocol/visitor.rs) | 0 | ||||
| -rw-r--r-- | tests/common/walker.rs | 6 | ||||
| -rw-r--r-- | tests/protocol_visitor_value.rs | 2 | ||||
| -rw-r--r-- | tests/protocol_walker_hint.rs | 28 | ||||
| -rw-r--r-- | tests/walker_struct.rs | 20 |
24 files changed, 759 insertions, 452 deletions
@@ -744,6 +744,7 @@ dependencies = [ "mockall", "pin-project", "proptest", + "serde", "serde_json", "tokio", ] @@ -14,6 +14,7 @@ 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", features = ["serde_derive"] } [features] default = ["std", "better_errors"] @@ -18,6 +18,10 @@ pub use type_name_id::*; #[cfg(all(feature = "alloc", not(feature = "std")))] use alloc::boxed::Box; +pub enum StaticType {} +pub enum NamedType {} +pub enum LifetimeType {} + higher_ranked_trait! { pub type class TypeName for<'a, 'ctx> { type Bound = &'a &'ctx (); diff --git a/src/build.rs b/src/build.rs index 3ce2b68..293e395 100644 --- a/src/build.rs +++ b/src/build.rs @@ -1,12 +1,14 @@ +use core::fmt::{Debug, Display}; pub mod builders; use crate::{ - effect::{Effect, ErasedEffective}, + effect::{Effect, EffectiveExt as _, ErasedEffective}, protocol::DynVisitor, + transform, Walker, WalkerTypes, }; /// A buildable type. -pub trait Build<'ctx, M, E: Effect>: Send + Sync { +pub trait Build<'ctx, M: 'ctx, E: Effect>: Sized + Send + Sync { /// The builder that can be used to build a value of `Self`. type Builder: Builder<'ctx, E, Value = Self>; } @@ -15,7 +17,7 @@ pub trait BuilderTypes { type Seed: Send + Sync; /// Error that can happen during filling the builder with data. - type Error: Send + Sync; + type Error: Send + Sync + Debug + Display; /// Type to be built. type Value: Send + Sync; diff --git a/src/build/builders/core.rs b/src/build/builders/core.rs index 41cffcb..2f285fa 100644 --- a/src/build/builders/core.rs +++ b/src/build/builders/core.rs @@ -1,4 +1,9 @@ -use crate::any_trait; +use crate::{ + any_trait, + effect::{Effect, Effective, EffectiveExt, ErasedEffective}, + protocol::{visitor::VisitResult, DynVisitor}, + DynWalkerObjSafe, Flow, Walker, +}; // pub mod array; pub mod bool; @@ -21,3 +26,16 @@ impl NoopVisitor { any_trait! { impl['ctx] NoopVisitor = [] } + +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> { + E::ready(NoopVisitor::new()).as_ctx_map(|noop| { + walker + .walk(DynVisitor(noop)) + .map(|x| x.to_continue().into()) + .cast() + }) + } +} diff --git a/src/build/builders/core/bool.rs b/src/build/builders/core/bool.rs index 60170e5..efe7188 100644 --- a/src/build/builders/core/bool.rs +++ b/src/build/builders/core/bool.rs @@ -1,4 +1,4 @@ -use core::marker::PhantomData; +use core::{fmt::Display, marker::PhantomData}; use crate::{ any::OwnedStatic, @@ -11,7 +11,7 @@ use crate::{ Flow, }; -impl<'ctx, M, E: Effect> crate::Build<'ctx, M, E> for bool { +impl<'ctx, M: 'ctx, E: Effect> crate::Build<'ctx, M, E> for bool { type Builder = Builder<E>; } @@ -20,6 +20,14 @@ pub enum Error { Incomplete, } +impl Display for Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Error::Incomplete => write!(f, "Incomplete"), + } + } +} + pub struct Builder<E>(Option<bool>, PhantomData<fn() -> E>); impl crate::BuilderTypes for bool { diff --git a/src/build/builders/core/struct.rs b/src/build/builders/core/struct.rs index b8ca483..aa5215e 100644 --- a/src/build/builders/core/struct.rs +++ b/src/build/builders/core/struct.rs @@ -1,115 +1,222 @@ -use core::ops::ControlFlow; +use core::fmt::{Debug, Display}; use crate::{ - any::{OwnedStatic, TempBorrowedStatic, TempBorrowedStaticHrt, TypeName}, + any::{AnyTrait, OwnedStatic, StaticType, TempBorrowedStatic, TempBorrowedStaticHrt, TypeName}, any_trait, effect::{ - Effect, EffectExt as _, Effective, EffectiveExt as _, ErasedEffective, ReadyExt as _, + Effect, EffectExt as _, Effective, EffectiveExt as _, ErasedEffective, ReadyExt as _, Ss, }, hkt::Marker, protocol::{ visitor::{ - tags, DynSequenceScope, Sequence, SequenceProto, Tag, TagProto, Value, ValueProto, - VisitResult, + tags, DynSequenceScope, EffectiveVisitExt as _, RequestHint, RequestHintProto, + Sequence, SequenceHint, SequenceProto, Tag, TagConst, TagHint, TagProto, Value, + ValueProto, VisitResult, }, - DynVisitor, + walker::{self, hint::hint_protocol}, + DynVisitor, DynWalker, }, tri, Builder, BuilderTypes, DynWalkerObjSafe, Flow, }; use super::NoopVisitor; -enum StructMode { - /// A tuple-like struct uses the order of the sequence. +/// A builder for a struct. +pub struct StructBuilder<'ctx, Info, Mode, E: Effect> +where + Info: StructTypeInfo<'ctx, Mode>, +{ + inner: Inner<'ctx, Info, Mode, E>, +} + +enum Inner<'ctx, Info, Mode, E: Effect> +where + Info: StructTypeInfo<'ctx, Mode>, +{ + Temp, + Seed(Info::Seed), + Builders { + /// The builders for all the struct's fields. + builders: Info::Builders<E>, + + /// The kind of struct the builder is expecting. + kind: StructKind, + }, + Value(Info::T), +} + +/// Structs are either tuple-like or map-like. +enum StructKind { + /// A tuple-like struct uses the order of the fields. Tuple, /// A map-like struct uses field names. Map, } -pub struct StructBuilder<'ctx, I: StructTypeInfo<'ctx, M, E>, M, E: Effect> { - builders: I::Builders, - mode: StructMode, - _generics: Marker<E>, -} +/// Information about how a struct type interacts with the [`StructBuilder`]. +/// +/// This trait is *not* implemented by the struct type itself to allow many +/// of these to exist per struct. +/// +/// The `Mode` generic allows implementations to change depending on the mode the user gives. +/// It is not used by the trait directly. +pub trait StructTypeInfo<'ctx, Mode>: 'static { + /// A struct of builders for each field. + type Builders<E: Effect>: Ss; -pub trait StructTypeInfo<'ctx, M, E: Effect>: 'static { - type Builders: Send + Sync; + /// The seed value needed to make the builders. + type Seed: Ss; - type Seed: Send + Sync; + /// An enum of the fields. + /// + /// These markers act in place of the field names. + type FieldMarker: Ss + Copy + Display; - type FieldMarker: Send + Sync + Copy + core::fmt::Debug; + /// The error type that can be generated while building the struct. + type Error: Ss + Debug + Display; - type Error: Send + Sync + core::fmt::Debug; + /// The kind of type for enabling the direct value protocol. + type ValueT: TypeName::MemberType; - type T: Send + Sync; + /// The struct type this info is for. + type T: Ss; - fn new_builders<'a>(seed: Self::Seed) -> ErasedEffective<'a, Self::Builders, E>; + const FIELD_COUNT: usize; - fn from_builders<'a>( - builders: Self::Builders, + /// Create a set of builders from a seed value. + fn new_builders<'a, E: Effect>(seed: Self::Seed) -> ErasedEffective<'a, Self::Builders<E>, E>; + + /// Finish building the struct value. + fn from_builders<'a, E: Effect>( + builders: Self::Builders<E>, ) -> ErasedEffective<'a, Result<Self::T, Self::Error>, E>; - fn as_visitor<'a>( + /// Get the visitor for a field. + /// + /// This is how [`StructBuilder`] picks a field builder to use. + fn as_visitor<'a, E: Effect>( marker: Self::FieldMarker, - builders: &'a mut Self::Builders, + builders: &'a mut Self::Builders<E>, ) -> DynVisitor<'a, 'ctx>; + /// Get a field marker from the index of the field. + /// + /// This should be the definition order as seen in the Rust source code. fn marker_from_index(index: usize) -> Option<Self::FieldMarker>; + + /// Get a field marker from a field name. fn marker_from_name(name: &str) -> Option<Self::FieldMarker>; + + /// Get the value from the value protocol. + fn from_value<'a>(value: TypeName::T<'a, 'ctx, Self::ValueT>) -> Self::T; } -pub struct StructError<'ctx, I: StructTypeInfo<'ctx, M, E>, M, E: Effect> { - error: I::Error, +/// Error that [`StructBuilder`] returns. +pub struct StructError<'ctx, Info, M> +where + Info: StructTypeInfo<'ctx, M>, +{ + /// Error from the struct info definition. + error: Info::Error, +} + +impl<'ctx, Info, Mode> StructError<'ctx, Info, Mode> +where + Info: StructTypeInfo<'ctx, Mode>, +{ + fn from_field_err(error: Info::Error) -> Self { + Self { error } + } } -impl<'ctx, I: StructTypeInfo<'ctx, M, E>, M, E: Effect> core::fmt::Debug - for StructError<'ctx, I, M, E> +impl<'ctx, Info, Mode> Debug for StructError<'ctx, Info, Mode> +where + Info: StructTypeInfo<'ctx, Mode>, { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("StructError") - .field("error", &self.error) - .finish() + f.debug_tuple("StructError").field(&self.error).finish() } } -impl<'ctx, I, M, E: Effect> BuilderTypes for StructBuilder<'ctx, I, M, E> +impl<'ctx, Info, Mode> Display for StructError<'ctx, Info, Mode> where - I: StructTypeInfo<'ctx, M, E>, + Info: StructTypeInfo<'ctx, Mode>, { - type Seed = I::Seed; + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + Display::fmt(&self.error, f) + } +} - type Error = StructError<'ctx, I, M, E>; +impl<'ctx, Info, Mode, E: Effect> BuilderTypes for StructBuilder<'ctx, Info, Mode, E> +where + Info: StructTypeInfo<'ctx, Mode>, +{ + type Seed = Info::Seed; + + type Error = StructError<'ctx, Info, Mode>; - type Value = I::T; + type Value = Info::T; } -impl<'ctx, I, M, E> Builder<'ctx, E> for StructBuilder<'ctx, I, M, E> +impl<'ctx, Info, Mode: 'ctx, E: Effect> StructBuilder<'ctx, Info, Mode, E> where - I: StructTypeInfo<'ctx, M, E>, - E: Effect, + Info: StructTypeInfo<'ctx, Mode>, +{ + fn into_builders<'e>(&mut self) -> ErasedEffective<'e, (), E> + where + 'ctx: 'e, + { + match core::mem::replace(&mut self.inner, Inner::Temp) { + Inner::Seed(seed) => Info::new_builders::<E>(seed).map(|builders| { + self.inner = Inner::Builders { + builders, + kind: StructKind::Tuple, + }; + }), + inner => { + self.inner = inner; + + ().ready() + } + } + } +} + +impl<'ctx, Info, Mode: 'ctx, E: Effect> Builder<'ctx, E> for StructBuilder<'ctx, Info, Mode, E> +where + Info: StructTypeInfo<'ctx, Mode>, { - #[inline(always)] fn from_seed<'a>(seed: Self::Seed) -> ErasedEffective<'a, Self, E> 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(), - } - }) + Self { + inner: Inner::Seed(seed), + } + .ready() } fn build<'a>(self) -> ErasedEffective<'a, Result<Self::Value, Self::Error>, E> where Self: 'a, { - I::from_builders(self.builders) - .map(|builders| builders.map_err(|err| StructError { error: err })) + match self.inner { + Inner::Temp => unreachable!(), + Inner::Seed(seed) => { + // We may be able to make a value from just the seed. + Info::new_builders::<E>(seed) + .then(|builders| Info::from_builders(builders)) + .map(|result| result.map_err(StructError::from_field_err)) + } + Inner::Builders { builders, .. } => { + // Create the value from the builders. + Info::from_builders(builders) + .map(|result| result.map_err(StructError::from_field_err)) + } + // Use the value as is. + Inner::Value(value) => Ok(value).ready(), + } } fn as_visitor(&mut self) -> DynVisitor<'_, 'ctx> { @@ -118,98 +225,237 @@ where } any_trait! { - impl['ctx, I, M, E] StructBuilder<'ctx, I, M, E> = [ + impl['ctx, Info, Mode, E] StructBuilder<'ctx, Info, Mode, E> = [ + RequestHintProto<E>, + ValueProto<Info::ValueT, E>, + TagProto<tags::Struct, E>, TagProto<tags::Map, E>, SequenceProto<E> ] where E: Effect, - I: StructTypeInfo<'ctx, M, E>, + Info: StructTypeInfo<'ctx, Mode>, + Mode: 'ctx, } -impl<'ctx, I, M, E> Tag<'ctx, tags::Map, E> for StructBuilder<'ctx, I, M, E> +impl<'ctx, Info, Mode: 'ctx, E> RequestHint<'ctx, E> for StructBuilder<'ctx, Info, Mode, E> where - I: StructTypeInfo<'ctx, M, E>, + Info: StructTypeInfo<'ctx, Mode>, E: Effect, { #[inline(always)] - fn visit<'a: 'c, 'b: 'c, 'c>( + fn request_hint<'a>( &'a mut self, - _kind: tags::Map, - walker: DynWalkerObjSafe<'b, 'ctx, E>, - ) -> ErasedEffective<'c, VisitResult<DynWalkerObjSafe<'b, 'ctx, E>>, E> { - // This signals to go into map mode for the sequence. - self.mode = StructMode::Map; - - E::ready(NoopVisitor::new()).as_ctx_map(|noop| { - walker - .walk(DynVisitor(noop)) - .map(|x| x.to_continue().into()) - .cast() + walker: DynWalker<'a, 'ctx>, + ) -> ErasedEffective<'a, VisitResult<DynWalker<'a, 'ctx>>, E> { + 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() + }) + .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>, _>( + walker.cast(), + *this, + TagHint { kind: TagConst }, + ) + .cast() + }) + .if_skipped(|(this, walker)| { + // 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>, _>( + walker.cast(), + *this, + TagHint { kind: TagConst }, + ) + .cast() + }) + .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>, _>( + walker.cast(), + *this, + SequenceHint { + len: (Info::FIELD_COUNT, Some(Info::FIELD_COUNT)), + }, + ) + .cast() + }) + .map(|((_, walker), result)| match result { + VisitResult::Skipped(()) => VisitResult::Skipped(walker), + VisitResult::Control(flow) => VisitResult::Control(flow), }) } } -// A struct is a sequence of field values. -impl<'ctx, I, M, E> Sequence<'ctx, E> for StructBuilder<'ctx, I, M, E> +/// Allows for a walker to directly give the struct value. +/// +/// This skips needing to go through each field individually. +impl<'ctx, Info, Mode, E> Value<'ctx, Info::ValueT, E> for StructBuilder<'ctx, Info, Mode, E> where - I: StructTypeInfo<'ctx, M, E>, + Info: StructTypeInfo<'ctx, Mode>, E: Effect, { - #[inline(always)] - fn visit<'a: 'c, 'b: 'c, 'c>( + fn visit<'a>( &'a mut self, - scope: DynSequenceScope<'b, 'ctx, E>, - ) -> ErasedEffective<'c, VisitResult<DynSequenceScope<'b, 'ctx, E>>, E> + value: TypeName::T<'a, 'ctx, Info::ValueT>, + ) -> ErasedEffective<'a, VisitResult<TypeName::T<'a, 'ctx, Info::ValueT>>, E> where - 'ctx: 'c, + TypeName::T<'a, 'ctx, Info::ValueT>: Send + Sized, + 'ctx: 'a, { - match self.mode { - StructMode::Tuple => { - // Tuple-like is based on the index of the field. - let mut index = 0; + // Get the value from what we got from the walker. + self.inner = Inner::Value(Info::from_value(value)); - // 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)); + // Since we have the struct value we are done. + E::ready(Flow::Done.into()) + } +} - // Move to the next field for the next iteration. - index += 1; +/// Allows for the walker to use field names. +/// +/// By default [`StructBuilder`] expects a tuple-like struct. +impl<'ctx, Info, Mode: 'ctx, E> Tag<'ctx, tags::Struct, E> for StructBuilder<'ctx, Info, Mode, E> +where + Info: StructTypeInfo<'ctx, Mode>, + E: Effect, +{ + fn visit<'this: 'e, 'walker: 'e, 'e>( + &'this mut self, + _kind: tags::Struct, + walker: DynWalkerObjSafe<'walker, 'ctx, E>, + ) -> ErasedEffective<'e, VisitResult<DynWalkerObjSafe<'walker, 'ctx, E>>, E> { + // If this protocol is used then we need to create the builders. + E::as_ctx(self, |this| this.into_builders().cast()).then(|(this, _)| { + match &mut this.inner { + Inner::Builders { kind, .. } => { + // This signals to go into map mode for the sequence. + *kind = StructKind::Map; + } + _ => {} + } - // Select the visitor for this field. - let visitor = I::as_visitor(marker, &mut this.builders); + // Walk the walker so nothing complains. + NoopVisitor::walk_dyn(walker).cast() + }) + } +} - // Visit the next item in the sequence. - scope.next(visitor).map(Flow::to_control_flow).cast() - }) +/// Allows for the walker to use field names. +/// +/// By default [`StructBuilder`] expects a tuple-like struct. +impl<'ctx, Info, Mode: 'ctx, E> Tag<'ctx, tags::Map, E> for StructBuilder<'ctx, Info, Mode, E> +where + Info: StructTypeInfo<'ctx, Mode>, + E: Effect, +{ + fn visit<'this: 'e, 'walker: 'e, 'e>( + &'this mut self, + _kind: tags::Map, + walker: DynWalkerObjSafe<'walker, 'ctx, E>, + ) -> ErasedEffective<'e, VisitResult<DynWalkerObjSafe<'walker, 'ctx, E>>, E> { + // If this protocol is used then we need to create the builders. + E::as_ctx(self, |this| this.into_builders().cast()).then(|(this, _)| { + match &mut this.inner { + Inner::Builders { kind, .. } => { + // This signals to go into map mode for the sequence. + *kind = StructKind::Map; + } + _ => {} } - 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(Into::into) + // Walk the walker so nothing complains. + NoopVisitor::walk_dyn(walker).cast() + }) + } +} + +/// Main protocol allowing a sequence of field values. +/// +/// By default this will use each sequence element as a field value. +/// +/// If the [`tags::Struct`] or [`tags::Map`] tags are used then this will expect +/// a sequence of key value pairs. Where the key is the field name. +impl<'ctx, Info, Mode: 'ctx, E> Sequence<'ctx, E> for StructBuilder<'ctx, Info, Mode, E> +where + Info: StructTypeInfo<'ctx, Mode>, + 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> + where + 'ctx: 'a + 'b + 'c, + { + // If this protocol is used then we need to create the builders. + E::as_ctx(self, |this| this.into_builders().cast()) + .as_ctx(|(this, _)| { + match &mut this.inner { + // We should treat the sequence as just values. + Inner::Builders { + builders, + kind: StructKind::Tuple, + } => { + // 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((scope, builders), |(scope, builders)| { + // Get the marker for the field at this index. + let marker = tri!(Info::marker_from_index(index)); + + // Move to the next field for the next iteration. + index += 1; + + // Select the visitor for this field. + let visitor = Info::as_visitor(marker, builders); + + // Visit the next item in the sequence. + scope.next(visitor).map(Flow::to_control_flow).cast() + }) + } + // We should treat the sequence as key value pairs. + Inner::Builders { + builders, + kind: StructKind::Map, + } => { + // A visitor that knows how to use field names. + let visitor = FieldVisitor::<Info, Mode, E> { + 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)| { + // Visit the next element of the sequence. + // When there are no more items in the sequence then the loop ends. + scope + .next(DynVisitor(visitor)) + .map(Flow::to_control_flow) + .cast() + }) + } + // If we don't have the builders ... we can't do anything. + // This would only happen if the walker gives the value directly + // then gives a sequence. + _ => Flow::Done.ready(), + } + .map(Into::into) + .cast() + }) + .remove_ctx() } } -struct FieldVisitor<'a, 'ctx, I: StructTypeInfo<'ctx, M, E>, M, E: Effect> { - builders: &'a mut I::Builders, +struct FieldVisitor<'a, 'ctx, I: StructTypeInfo<'ctx, M>, M, E: Effect> { + builders: &'a mut I::Builders<E>, marker: Option<I::FieldMarker>, _marker: Marker<E>, } @@ -228,15 +474,14 @@ any_trait! { }) } where E: Effect, - I: StructTypeInfo<'ctx, M, E>, + I: StructTypeInfo<'ctx, M>, } impl<'d, 'ctx, I, M, E> Tag<'ctx, tags::Key, E> for FieldVisitor<'d, 'ctx, I, M, E> where E: Effect, - I: StructTypeInfo<'ctx, M, E>, + I: StructTypeInfo<'ctx, M>, { - #[inline(always)] fn visit<'a: 'c, 'b: 'c, 'c>( &'a mut self, _key: tags::Key, @@ -256,7 +501,7 @@ where } } -struct NameVisitor<'ctx, I: StructTypeInfo<'ctx, M, E>, M, E: Effect> { +struct NameVisitor<'ctx, I: StructTypeInfo<'ctx, M>, M, E: Effect> { field_marker: Option<I::FieldMarker>, _marker: Marker<E>, } @@ -268,15 +513,14 @@ any_trait! { ValueProto<OwnedStatic<&'static str>, E>, ] where E: Effect, - I: StructTypeInfo<'ctx, M, E>, + I: StructTypeInfo<'ctx, M>, } impl<'ctx, I, M, E> Value<'ctx, OwnedStatic<usize>, E> for NameVisitor<'ctx, I, M, E> where E: Effect, - I: StructTypeInfo<'ctx, M, E>, + I: StructTypeInfo<'ctx, M>, { - #[inline(always)] fn visit<'a>( &'a mut self, OwnedStatic(index): TypeName::T<'a, 'ctx, OwnedStatic<usize>>, @@ -294,9 +538,8 @@ where impl<'ctx, I, M, E> Value<'ctx, TempBorrowedStaticHrt<str>, E> for NameVisitor<'ctx, I, M, E> where E: Effect, - I: StructTypeInfo<'ctx, M, E>, + I: StructTypeInfo<'ctx, M>, { - #[inline(always)] fn visit<'a>( &'a mut self, TempBorrowedStatic(name): TypeName::T<'a, 'ctx, TempBorrowedStaticHrt<str>>, @@ -314,9 +557,8 @@ where impl<'ctx, I, M, E> Value<'ctx, OwnedStatic<&'static str>, E> for NameVisitor<'ctx, I, M, E> where E: Effect, - I: StructTypeInfo<'ctx, M, E>, + I: StructTypeInfo<'ctx, M>, { - #[inline(always)] fn visit<'a>( &'a mut self, OwnedStatic(name): TypeName::T<'a, 'ctx, OwnedStatic<&'static str>>, diff --git a/src/effect.rs b/src/effect.rs index 458ed8a..a994bcd 100644 --- a/src/effect.rs +++ b/src/effect.rs @@ -261,6 +261,14 @@ pub trait EffectiveExt<'lt>: Effective<'lt> { self.map(|(_, value)| value) } + fn remove_value<'wrap, Ctx: Ss, T: Ss>(self) -> ErasedEffective<'wrap, Ctx, Self::Effect> + where + Self: Effective<'lt, Output = (Ctx, T)>, + 'lt: 'wrap, + { + self.map(|(ctx, _)| ctx) + } + fn map<'wrap, U: Ss, F: Ss>(self, f: F) -> ErasedEffective<'wrap, U, Self::Effect> where F: FnOnce(Self::Output) -> U, @@ -123,7 +123,7 @@ macro_rules! Walk { } => { const _: () = { impl<'ctx, M: 'ctx, E: $crate::effect::Effect> $crate::Walk<'ctx, M, E> for &'ctx $name { - type Walker = $crate::walkers::core::r#struct::StructWalker<'ctx, Info, $crate::walkers::core::r#struct::StaticType, M, E>; + type Walker = $crate::walkers::core::r#struct::StructWalker<'ctx, Info, $crate::any::StaticType, M, E>; fn into_walker(self) -> Self::Walker { $crate::walkers::core::r#struct::StructWalker::new(self) @@ -153,7 +153,7 @@ macro_rules! Walk { type FieldError = FieldError<'ctx>; type T = $name; - type S = $crate::walkers::core::r#struct::StaticType; + type S = $crate::any::StaticType; #[allow(unreachable_code, non_snake_case, non_upper_case_globals, non_camel_case_types)] fn walk_field<'a, E: $crate::effect::Effect>( @@ -242,44 +242,38 @@ macro_rules! Walk { // } // } - 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, + transform, Build, BuildExt as _, DefaultMode, }; + use crate::{Walk, WalkExt as _}; + use macro_rules_attribute::derive; - #[derive(Walk!, Debug)] + #[derive(Walk!)] pub struct X { pub a: bool, pub b: bool, - pub c: bool, } - #[derive(Build!, Debug, PartialEq)] + #[derive(Build!, PartialEq, Debug)] pub struct Y { - pub a: bool, pub b: bool, - pub c: bool, + pub a: 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()); + // Y::build(x.as_walker()).unwrap() + x.walk(Y::new_builder()).unwrap() + } - other.0.unwrap() + #[test] + fn demo() { + assert_eq!(ident(X { a: true, b: false }), Y { a: true, b: false }); } } diff --git a/src/macros/build.rs b/src/macros/build.rs index b990f08..518cf41 100644 --- a/src/macros/build.rs +++ b/src/macros/build.rs @@ -1,5 +1,3 @@ -use crate::{effect::blocking::Blocking, transform, DefaultMode}; - #[macro_export] macro_rules! Build { { @@ -9,8 +7,8 @@ macro_rules! Build { ),* $(,)?} } => { const _: () = { - impl<'ctx, M, E: $crate::effect::Effect> $crate::Build<'ctx, M, E> for $name { - type Builder = $crate::builders::core::r#struct::StructBuilder<'ctx, Info, M, E>; + impl<'ctx, M: 'ctx, E: $crate::effect::Effect> $crate::Build<'ctx, M, E> for $name { + type Builder = $crate::builders::core::r#struct::StructBuilder<'ctx, __Info, M, E>; } $vis struct Builders<'ctx, M, E: $crate::effect::Effect> { @@ -35,17 +33,38 @@ macro_rules! Build { $($field(<$type as $crate::BuilderTypes>::Error)),* } - $vis struct Info; + impl ::core::fmt::Display for Field { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + f.write_str(match self { + $(Field::$field => stringify!($field)),* + }) + } + } + + impl ::core::fmt::Display for Error { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + f.write_str(match self { + $(Error::$field(_) => stringify!($field)),* + }) + } + } + + $vis struct __Info; - impl<'ctx, M, E: $crate::effect::Effect> $crate::builders::core::r#struct::StructTypeInfo<'ctx, M, E> for Info { - type Builders = Builders<'ctx, M, E>; + impl<'ctx, M: 'ctx> $crate::builders::core::r#struct::StructTypeInfo<'ctx, M> for __Info { + type Builders<E: $crate::effect::Effect> = Builders<'ctx, M, E>; type FieldMarker = Field; type T = $name; type Error = Error; type Seed = ($(<$type as $crate::BuilderTypes>::Seed),*); + type ValueT = $crate::any::OwnedStatic<$name>; + + const FIELD_COUNT: usize = { + [$(stringify!($field)),*].len() + }; #[inline(always)] - fn new_builders<'a>(seed: Self::Seed) -> $crate::effect::ErasedEffective<'a, Self::Builders, E> { + fn new_builders<'a, E: $crate::effect::Effect>(seed: Self::Seed) -> $crate::effect::ErasedEffective<'a, Self::Builders<E>, E> { let ($($field),*) = seed; use $crate::effect::EffectiveExt; @@ -57,15 +76,9 @@ macro_rules! Build { $($field),* } }) - - // E::wrap(async move { - // Builders { - // $($field: $crate::Builder::<E>::from_seed($field).await),* - // } - // }) } - fn from_builders<'a>(builders: Self::Builders) -> $crate::effect::ErasedEffective<'a, Result<Self::T, Self::Error>, E> { + fn from_builders<'a, E: $crate::effect::Effect>(builders: Self::Builders<E>) -> $crate::effect::ErasedEffective<'a, Result<Self::T, Self::Error>, E> { use $crate::Builder; use $crate::effect::EffectiveExt; @@ -79,17 +92,11 @@ macro_rules! Build { }), Err(err) => Err(err) }) - - // E::wrap(async { - // Ok($name { - // $($field: builders.$field.build().await.map_err(Error::$field)?),* - // }) - // }) } - fn as_visitor<'a>( + fn as_visitor<'a, E: $crate::effect::Effect>( marker: Self::FieldMarker, - builders: &'a mut Self::Builders, + builders: &'a mut Self::Builders<E>, ) -> $crate::protocol::DynVisitor<'a, 'ctx> { use $crate::Builder; @@ -111,37 +118,11 @@ macro_rules! Build { _ => None } } + + fn from_value(value: Self::ValueT) -> Self::T { + value.0 + } } }; } } - -#[test] -fn demo() { - use crate::Walk; - use macro_rules_attribute::derive; - - #[derive(Build!, Walk!, Debug)] - struct X { - a: bool, - b: bool, - } - - #[derive(Build!, Walk!, Debug)] - struct Y { - b: bool, - a: bool, - } - - let value = X { a: true, b: false }; - - let other = transform::<<Y as crate::Build<'_, DefaultMode, _>>::Builder, _, Blocking>( - ((), ()), - Walk::<DefaultMode, _>::into_walker(&value), - ) - .value(); - - dbg!(other); - - // todo!(); -} diff --git a/src/protocol.rs b/src/protocol.rs index 822d827..60db903 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -84,8 +84,8 @@ impl<'a, 'ctx> DerefMut for DynVisitor<'a, 'ctx> { pub struct DynWalker<'a, 'ctx>(pub &'a mut (dyn AnyTrait<'ctx> + Send + Sync + 'a)); impl<'a, 'ctx> DynWalker<'a, 'ctx> { - pub fn cast<'b>(&'b mut self) -> DynVisitor<'b, 'ctx> { - DynVisitor(&mut *self.0) + pub fn cast<'b>(&'b mut self) -> DynWalker<'b, 'ctx> { + DynWalker(&mut *self.0) } } diff --git a/src/protocol/walker/hint.rs b/src/protocol/walker/hint.rs index 8707923..148b8f2 100644 --- a/src/protocol/walker/hint.rs +++ b/src/protocol/walker/hint.rs @@ -5,11 +5,11 @@ //! to the walker about what it is expecting. use crate::{ - any::TypeName, - effect::{Effect, ErasedEffective}, + any::{AnyTrait, TypeName}, + effect::{Effect, EffectiveExt as _, ErasedEffective, ReadyExt as _, Ss}, higher_ranked_trait, higher_ranked_type, hkt::Marker, - protocol::DynVisitor, + protocol::{visitor::VisitResult, DynVisitor, DynWalker}, Flow, }; @@ -54,11 +54,13 @@ pub trait Hint<'ctx, Protocol: ?Sized + HintMeta> { /// Hint to the walker to use the `P` protocol. /// /// This should only be called once per [`RequestHint`]. - fn hint<'a>( - &'a mut self, - visitor: DynVisitor<'a, 'ctx>, - hint: MetaHint<'a, 'ctx, Protocol>, - ) -> ErasedEffective<'a, Flow, Protocol::Effect>; + fn hint<'this: 'e, 'visitor: 'e, 'hint: 'e, 'e>( + &'this mut self, + visitor: DynVisitor<'visitor, 'ctx>, + hint: MetaHint<'hint, 'ctx, Protocol>, + ) -> ErasedEffective<'e, Flow, Protocol::Effect> + where + 'ctx: 'this + 'visitor + 'hint + 'e; /// Ask the walker for information about it's support of the protocol. fn known<'a>( @@ -74,13 +76,33 @@ higher_ranked_type! { impl['a, 'ctx, Protocol] type T['a, 'ctx] for HintProto<Protocol> = dyn Hint<'ctx, Protocol> + Send + Sync + 'a where { - Protocol: 'static, + Protocol: ?Sized + 'static, }; impl['a, 'ctx, Protocol] type HigherRanked['a, 'ctx] for dyn Hint<'ctx, Protocol> + Send + Sync + 'a = HintProto<Protocol> where { - Protocol: 'static, + Protocol: ?Sized + 'static, }; } } + +pub fn hint_protocol< + 'ctx, + 'walker: 'e, + 'visitor: 'e, + 'hint: 'e, + 'e, + Protocol: ?Sized + HintMeta, + V: AnyTrait<'ctx> + Ss, +>( + walker: DynWalker<'walker, 'ctx>, + visitor: &'visitor mut V, + hint: MetaHint<'hint, 'ctx, Protocol>, +) -> ErasedEffective<'e, VisitResult<()>, Protocol::Effect> { + if let Some(object) = walker.0.upcast_mut::<HintProto<Protocol>>() { + object.hint(DynVisitor(visitor), hint).map(Into::into) + } else { + VisitResult::Skipped(()).ready() + } +} diff --git a/src/transform.rs b/src/transform.rs index caf24ab..8e16b91 100644 --- a/src/transform.rs +++ b/src/transform.rs @@ -2,9 +2,9 @@ use core::marker::PhantomData; use crate::{ build::Builder, - effect::{Effect, Effective, EffectiveExt, ErasedEffective}, + effect::{blocking::Blocking, Effect, Effective, EffectiveExt, ErasedEffective}, hkt::Marker, - BuilderTypes, Walk, Walker, WalkerTypes, + Build, BuilderTypes, DefaultMode, Walk, Walker, WalkerTypes, }; #[inline(always)] @@ -36,6 +36,80 @@ pub fn transform<'a, 'ctx: 'a, B: Builder<'ctx, E> + 'a, W: Walker<'ctx, E> + 'a // }) } +pub enum BuildError<B, W> +where + B: BuilderTypes, + W: WalkerTypes, +{ + Builder(B::Error), + Both(B::Error, W::Error), +} + +impl<B, W> core::fmt::Debug for BuildError<B, W> +where + B: BuilderTypes, + W: WalkerTypes, +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Builder(arg0) => f.debug_tuple("Builder").field(arg0).finish(), + Self::Both(arg0, arg1) => f.debug_tuple("Both").field(arg0).field(arg1).finish(), + } + } +} + +pub trait BuildExt { + /// Build a value of this type using the default builder. + fn build<'ctx, W>(walker: W) -> Result<Self, BuildError<Self::Builder, W>> + where + Self: Build<'ctx, DefaultMode, Blocking>, + <Self::Builder as BuilderTypes>::Seed: Default, + W: Walker<'ctx, Blocking>, + { + match transform::<Self::Builder, _, _>(Default::default(), walker).value() { + (Ok(value), _) => Ok(value), + (Err(err), Ok(_)) => Err(BuildError::Builder(err)), + (Err(build_err), Err(walker_err)) => Err(BuildError::Both(build_err, walker_err)), + } + } + + fn new_builder<'ctx>() -> Self::Builder + where + Self: Build<'ctx, DefaultMode, Blocking>, + <Self::Builder as BuilderTypes>::Seed: Default, + { + Self::Builder::from_seed(Default::default()).value() + } +} + +impl<T> BuildExt for T {} + +pub trait WalkExt { + fn as_walker<'ctx: 'a, 'a, E: Effect>( + &'a self, + ) -> <&'a Self as Walk<'ctx, DefaultMode, E>>::Walker + where + &'a Self: Walk<'ctx, DefaultMode, E>, + { + Walk::into_walker(self) + } + + fn walk<'ctx: 'a, 'a, B>(&'a self, builder: B) -> Result<B::Value, ()> + where + &'a Self: Walk<'ctx, DefaultMode, Blocking>, + B: Builder<'ctx, Blocking>, + { + let mut builder = builder; + Walk::into_walker(self).walk(builder.as_visitor()); + match builder.build().value() { + Ok(value) => Ok(value), + _ => todo!() + } + } +} + +impl<T> WalkExt for T {} + /// For use in a lens. pub struct Projection<T, B, U, M> { value: T, diff --git a/src/walk.rs b/src/walk.rs index 9c6cd10..d15c6f4 100644 --- a/src/walk.rs +++ b/src/walk.rs @@ -1,5 +1,7 @@ pub mod walkers; +use core::fmt::{Debug, Display}; + use crate::{ effect::{Effect, Effective, EffectiveExt, ErasedEffective}, protocol::DynVisitor, @@ -16,7 +18,7 @@ pub trait Walk<'ctx, M, E: Effect>: Sized { } pub trait WalkerTypes { - type Error: Send + Sync; + type Error: Send + Sync + Debug; /// An arbitrary type the walker is left with after walking. /// diff --git a/src/walk/walkers/core/struct.rs b/src/walk/walkers/core/struct.rs index 393450f..45fba0e 100644 --- a/src/walk/walkers/core/struct.rs +++ b/src/walk/walkers/core/struct.rs @@ -1,7 +1,7 @@ use core::{any::TypeId, marker::PhantomData}; use crate::{ - any::{AnyTrait, BorrowedStatic, BorrowedStaticHrt}, + any::{AnyTrait, BorrowedStatic, BorrowedStaticHrt, StaticType}, any_trait, effect::{Effect, EffectExt as _, Effective, EffectiveExt, ErasedEffective, ReadyExt as _}, hkt::Marker, @@ -21,10 +21,6 @@ use crate::{ use super::{noop::NoopWalker, tag::StaticSliceWalker, value::ValueWalker}; -pub enum StaticType {} -pub enum NamedType {} -pub enum LifetimeType {} - /// Walker for a borrow of a struct. /// /// This walker implements the struct flow. The struct cannot contain lifetimes. @@ -52,7 +48,7 @@ pub trait StructTypeInfo<'ctx, M>: 'static { const FIELDS: &'static [&'static str]; /// The walking errors for the fields. - type FieldError: Send + Sync; + type FieldError: Send + Sync + core::fmt::Debug; type S: 'static; @@ -192,11 +188,14 @@ where Self: AnyTrait<'ctx> + RecoverableScope<'ctx, E>, { #[inline(always)] - fn hint<'a>( - &'a mut self, - visitor: DynVisitor<'a, 'ctx>, - _hint: <RecoverableProto<E> as HintMeta>::Hint, - ) -> ErasedEffective<'a, Flow, E> { + fn hint<'this, 'visitor, 'hint, 'e>( + &'this mut self, + visitor: DynVisitor<'visitor, 'ctx>, + hint: MetaHint<'hint, 'ctx, RecoverableProto<E>>, + ) -> ErasedEffective<'e, Flow, E> + where + 'ctx: 'this + 'visitor + 'hint + 'e, + { todo!() // E::map( // visit_recoverable::<E>(visitor, self), @@ -223,11 +222,14 @@ where I: StructTypeInfo<'ctx, M, S = S>, { #[inline(always)] - fn hint<'a>( - &'a mut self, - visitor: DynVisitor<'a, 'ctx>, - _hint: MetaHint<'a, 'ctx, TagProto<TagConst<{ TAG_FIELD_NAMES.to_int() }>, E>>, - ) -> ErasedEffective<'a, Flow, E> { + fn hint<'this, 'visitor, 'hint, 'e>( + &'this mut self, + visitor: DynVisitor<'visitor, 'ctx>, + hint: MetaHint<'hint, 'ctx, TagProto<TagConst<{ TAG_FIELD_NAMES.to_int() }>, E>>, + ) -> ErasedEffective<'e, Flow, E> + where + 'ctx: 'this + 'visitor + 'hint + 'e, + { todo!() // E::map( // visit_tag::<TagConst<{ TAG_FIELD_NAMES.to_int() }>, E, _>( @@ -268,11 +270,14 @@ where I: StructTypeInfo<'ctx, M, S = S>, { #[inline(always)] - fn hint<'a>( - &'a mut self, - visitor: DynVisitor<'a, 'ctx>, - _hint: <TagProto<TagConst<{ TAG_TYPE_NAME.to_int() }>, E> as HintMeta>::Hint, - ) -> ErasedEffective<'a, Flow, E> { + fn hint<'this, 'visitor, 'hint, 'e>( + &'this mut self, + visitor: DynVisitor<'visitor, 'ctx>, + hint: MetaHint<'hint, 'ctx, TagProto<TagConst<{ TAG_TYPE_NAME.to_int() }>, E>>, + ) -> ErasedEffective<'e, Flow, E> + where + 'ctx: 'this + 'visitor + 'hint + 'e, + { todo!() // E::map( // visit_tag::<TagConst<{ TAG_TYPE_NAME.to_int() }>, E, _>( @@ -313,11 +318,14 @@ where I: StructTypeInfo<'ctx, M, S = S>, { #[inline(always)] - fn hint<'a>( - &'a mut self, - visitor: DynVisitor<'a, 'ctx>, - _hint: <TagProto<TagConst<{ TAG_MAP.to_int() }>, E> as HintMeta>::Hint, - ) -> ErasedEffective<'a, Flow, E> { + fn hint<'this, 'visitor, 'hint, 'e>( + &'this mut self, + visitor: DynVisitor<'visitor, 'ctx>, + hint: MetaHint<'hint, 'ctx, TagProto<TagConst<{ TAG_MAP.to_int() }>, E>>, + ) -> ErasedEffective<'e, Flow, E> + where + 'ctx: 'this + 'visitor + 'hint + 'e, + { todo!() // E::map( // visit_tag::<TagConst<{ TAG_MAP.to_int() }>, E, _>(TagConst, visitor, NoopWalker::new()), @@ -354,27 +362,35 @@ where I: StructTypeInfo<'ctx, M, S = S>, { #[inline(always)] - fn hint<'a>( - &'a mut self, - visitor: DynVisitor<'a, 'ctx>, - _hint: <TagProto<TagConst<{ TAG_STRUCT.to_int() }>, E> as HintMeta>::Hint, - ) -> ErasedEffective<'a, Flow, E> { - todo!() - // E::map( - // visit_tag::<TagConst<{ TAG_STRUCT.to_int() }>, E, _>( - // TagConst, - // visitor, - // NoopWalker::new(), - // ), - // |status| match status { - // Err(err) => { - // self.error = Some(StructWalkErrorKind::Tag(err)); - // Flow::Err - // } - // Ok(VisitResult::Skipped(_)) => Flow::Continue, - // Ok(VisitResult::Control(flow)) => flow, - // }, - // ) + fn hint<'this: 'e, 'visitor: 'e, 'hint: 'e, 'e>( + &'this mut self, + visitor: DynVisitor<'visitor, 'ctx>, + hint: MetaHint<'hint, 'ctx, TagProto<TagConst<{ TAG_STRUCT.to_int() }>, E>>, + ) -> ErasedEffective<'e, Flow, E> + where + 'ctx: 'this + 'visitor + 'hint + 'e, + { + E::as_ctx( + (self, visitor), + #[inline(always)] + |(this, visitor)| { + visit_tag::<TagConst<{ TAG_STRUCT.to_int() }>, E, _>( + TagConst, + visitor.cast(), + NoopWalker::new(), + ) + .map(|status| match status { + Err(err) => { + this.error = Some(StructWalkErrorKind::Tag(err)); + Flow::Err + } + Ok(VisitResult::Skipped(_)) => Flow::Continue, + Ok(VisitResult::Control(flow)) => flow, + }) + .cast() + }, + ) + .remove_ctx() } #[inline(always)] @@ -400,11 +416,14 @@ where I::T: 'static, { #[inline(always)] - fn hint<'a>( - &'a mut self, - visitor: DynVisitor<'a, 'ctx>, - _hint: <TagProto<TagConst<{ TAG_TYPE_ID.to_int() }>, E> as HintMeta>::Hint, - ) -> ErasedEffective<'a, Flow, E> { + fn hint<'this, 'visitor, 'hint, 'e>( + &'this mut self, + visitor: DynVisitor<'visitor, 'ctx>, + hint: MetaHint<'hint, 'ctx, TagProto<TagConst<{ TAG_TYPE_ID.to_int() }>, E>>, + ) -> ErasedEffective<'e, Flow, E> + where + 'ctx: 'this + 'visitor + 'hint + 'e, + { todo!() // E::map( // visit_tag::<TagConst<{ TAG_TYPE_ID.to_int() }>, E, _>( @@ -445,11 +464,14 @@ where I::T: 'static, { #[inline(always)] - fn hint<'a>( - &'a mut self, - visitor: DynVisitor<'a, 'ctx>, - hint: <TagProto<TagDyn, E> as HintMeta>::Hint, - ) -> ErasedEffective<'a, Flow, E> { + fn hint<'this: 'e, 'visitor: 'e, 'hint: 'e, 'e>( + &'this mut self, + visitor: DynVisitor<'visitor, 'ctx>, + hint: MetaHint<'hint, 'ctx, TagProto<TagDyn, E>>, + ) -> ErasedEffective<'e, Flow, E> + where + 'ctx: 'this + 'visitor + 'hint + 'e, + { match hint.kind.0 { crate::TAG_TYPE_ID => { Hint::<'ctx, TagProto<TagConst<{ TAG_TYPE_ID.to_int() }>, E>>::hint( @@ -509,11 +531,14 @@ where I::T: 'static, { #[inline(always)] - fn hint<'a>( - &'a mut self, - visitor: DynVisitor<'a, 'ctx>, - _hint: (), - ) -> ErasedEffective<'a, Flow, E> { + fn hint<'this, 'visitor, 'hint, 'e>( + &'this mut self, + visitor: DynVisitor<'visitor, 'ctx>, + hint: MetaHint<'hint, 'ctx, ValueProto<BorrowedStaticHrt<I::T>, E>>, + ) -> ErasedEffective<'e, Flow, E> + where + 'ctx: 'this + 'visitor + 'hint + 'e, + { todo!() // E::map( // visit_value::<_, E>(visitor, BorrowedStatic(self.value)), @@ -533,22 +558,29 @@ where } } -impl<'ctx, I, S, M, E> Hint<'ctx, SequenceProto<E>> for StructWalker<'ctx, I, S, M, E> +impl<'ctx, I, S, M: 'ctx, E> Hint<'ctx, SequenceProto<E>> for StructWalker<'ctx, I, S, M, E> where E: Effect, I: StructTypeInfo<'ctx, M, S = S>, { #[inline(always)] - fn hint<'a>( - &'a mut self, - visitor: DynVisitor<'a, 'ctx>, - _hint: <SequenceProto<E> as HintMeta>::Hint, - ) -> ErasedEffective<'a, Flow, E> { - todo!() - // E::map(visit_sequence::<E>(visitor, self), |status| match status { - // VisitResult::Skipped(_) => Flow::Continue, - // VisitResult::Control(flow) => flow, - // }) + #[inline(always)] + fn hint<'this: 'e, 'visitor: 'e, 'hint: 'e, 'e>( + &'this mut self, + visitor: DynVisitor<'visitor, 'ctx>, + hint: MetaHint<'hint, 'ctx, SequenceProto<E>>, + ) -> ErasedEffective<'e, Flow, E> + where + 'ctx: 'this + 'visitor + 'hint + 'e, + { + E::as_ctx_map((self, visitor), |(this, visitor)| { + visit_sequence::<E>(visitor.cast(), *this) + .map(|status| match status { + VisitResult::Skipped(_) => Flow::Continue, + VisitResult::Control(flow) => flow, + }) + .cast() + }) } #[inline(always)] diff --git a/tests/builder_struct.rs b/tests/builder_struct.rs index 290676b..f77bd52 100644 --- a/tests/builder_struct.rs +++ b/tests/builder_struct.rs @@ -1,3 +1,4 @@ +use macro_rules_attribute::derive; use treaty::{ any::{OwnedStatic, TempBorrowedStatic}, builders::{self, core::r#struct::StructBuilder}, @@ -9,10 +10,7 @@ use treaty::{ transform, walkers::{ self, - core::{ - noop::NoopWalker, - r#struct::{StaticType, StructWalker}, - }, + core::{noop::NoopWalker, r#struct::StructWalker}, }, Build, Builder, DefaultMode, Flow, Walk, Walker, }; @@ -21,136 +19,19 @@ use crate::common::{protocol::sequence::MockSequenceScope, walker::MockWalker}; mod common; -#[derive(Debug, PartialEq)] +#[derive(Build!, Walk!, Debug, PartialEq)] struct X { a: bool, b: bool, } -struct Info; - -impl<'ctx, M> walkers::core::r#struct::StructTypeInfo<'ctx, M> for Info { - const NAME: &'static str = "X"; - - const FIELDS: &'static [&'static str] = &["a", "b"]; - - type FieldError = (); - - type S = StaticType; - - type T = X; - - #[inline(always)] - fn walk_field<'a, E: Effect>( - index: usize, - value: &'ctx Self::T, - visitor: DynVisitor<'a, 'ctx>, - ) -> ErasedEffective<'a, Result<Flow, Self::FieldError>, E> { - todo!() - // E::wrap(async move { - // match index { - // 0 => { - // let walker = <&bool as Walk<M, E>>::into_walker(&value.a); - // - // assert_eq!(Walker::<E>::walk(walker, visitor).await, Ok(())); - // - // Ok(Flow::Continue) - // } - // 1 => { - // let walker = <&bool as Walk<M, E>>::into_walker(&value.b); - // - // assert_eq!(Walker::<E>::walk(walker, visitor).await, Ok(())); - // - // Ok(Flow::Continue) - // } - // _ => Ok(Flow::Done), - // } - // }) - } -} - -struct Fields<'ctx, M, E: Effect> { - a: <bool as Build<'ctx, M, E>>::Builder, - b: <bool as Build<'ctx, M, E>>::Builder, -} - -#[derive(Copy, Clone, Debug)] -enum FieldMarker { - A, - B, -} - -impl<'ctx, M, E: Effect> builders::core::r#struct::StructTypeInfo<'ctx, M, E> for Info { - type Builders = Fields<'ctx, M, E>; - - type FieldMarker = FieldMarker; - - type T = X; - - #[inline(always)] - fn marker_from_index(index: usize) -> Option<Self::FieldMarker> { - match index { - 0 => Some(FieldMarker::A), - 1 => Some(FieldMarker::B), - _ => None, - } - } - - #[inline(always)] - fn marker_from_name(name: &str) -> Option<Self::FieldMarker> { - match name { - "a" => Some(FieldMarker::A), - "b" => Some(FieldMarker::B), - _ => None, - } - } - - type Error = (); - - #[inline(always)] - fn from_builders<'a>( - builders: Self::Builders, - ) -> ErasedEffective<'a, Result<Self::T, Self::Error>, E> { - E::from_future(async { - Ok(X { - a: builders.a.build().into_future().await.unwrap(), - b: builders.b.build().into_future().await.unwrap(), - }) - }) - } - - #[inline(always)] - fn as_visitor<'a>( - marker: Self::FieldMarker, - builders: &'a mut Self::Builders, - ) -> DynVisitor<'a, 'ctx> { - match marker { - FieldMarker::A => builders.a.as_visitor(), - FieldMarker::B => builders.b.as_visitor(), - } - } - - type Seed = (); - - #[inline(always)] - fn new_builders<'a>(_seed: Self::Seed) -> ErasedEffective<'a, Self::Builders, E> { - E::from_future(async { - Fields { - a: Builder::<E>::from_seed(()).into_future().await, - b: Builder::<E>::from_seed(()).into_future().await, - } - }) - } -} - #[test] -#[ignore] fn demo() { let value = X { a: true, b: false }; - let (other, _) = transform::<StructBuilder<Info, DefaultMode, _>, _, Blocking>( - (), - StructWalker::<Info, _, DefaultMode, _>::new(&value), + let (other, _) = transform::<<X as Build<DefaultMode, _>>::Builder, _, Blocking>( + ((), ()), + <&X as Walk<DefaultMode, _>>::Walker::new(&value), ) .value(); @@ -190,7 +71,7 @@ fn from_basic_tuple_like() { Flow::Done }); - let mut builder = StructBuilder::<Info, DefaultMode, Blocking>::from_seed(()).value(); + let mut builder = <X as Build<DefaultMode, Blocking>>::Builder::from_seed(((), ())).value(); let visitor = builder.as_visitor(); // Visit the sequence of field values. @@ -270,7 +151,7 @@ fn from_basic_map_like() { // There are no more fields. scope.expect_next().once().returning(|_visitor| Flow::Done); - let mut builder = StructBuilder::<Info, DefaultMode, Blocking>::from_seed(()).value(); + let mut builder = <X as Build<DefaultMode, Blocking>>::Builder::from_seed(((), ())).value(); let mut visitor = builder.as_visitor(); // We need to provide the map tag to the struct before getting into the sequence. @@ -333,6 +214,17 @@ pub mod demo { #[test] fn demo() { - assert_eq!(ident(X { a: true, b: false, c: true }), Y { a: true, b: false, c: true }); + 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 35bc8a8..f86e4c7 100644 --- a/tests/common/builder.rs +++ b/tests/common/builder.rs @@ -1,3 +1,4 @@ +use core::fmt::{Debug, Display}; use mockall::mock; use treaty::{ any::{indirect, AnyTrait, AnyTraitObject, TypeNameId}, @@ -12,6 +13,15 @@ use self::__mock_MockBuilder::__from_seed::Context; use super::ContextGuard; +#[derive(Debug)] +pub struct EmptyError; + +impl Display for EmptyError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "EmptyError") + } +} + mock! { pub Builder<Seed: 'static, Value: 'static, Error: 'static> { pub fn from_seed(seed: Seed) -> Self; @@ -22,7 +32,7 @@ mock! { } } -impl<Seed: Send + Sync, Value: Send + Sync, Error: Send + Sync> BuilderTypes +impl<Seed: Send + Sync, Value: Send + Sync, Error: Send + Sync + Display + Debug> BuilderTypes for MockBuilder<Seed, Value, Error> { type Seed = Seed; @@ -46,8 +56,13 @@ impl<Seed: 'static, Value: 'static, Error: 'static> MockBuilder<Seed, Value, Err } } -impl<'ctx, Seed: Send + Sync, Value: Send + Sync, Error: Send + Sync, E: Effect> Builder<'ctx, E> - for MockBuilder<Seed, Value, Error> +impl< + 'ctx, + Seed: Send + Sync, + Value: Send + Sync, + Error: Send + Sync + Display + Debug, + E: Effect, + > Builder<'ctx, E> for MockBuilder<Seed, Value, Error> { fn from_seed<'a>(seed: Self::Seed) -> ErasedEffective<'a, Self, E> where diff --git a/tests/common/protocol.rs b/tests/common/protocol.rs index 38c0c61..3dea0b1 100644 --- a/tests/common/protocol.rs +++ b/tests/common/protocol.rs @@ -3,4 +3,4 @@ pub mod recoverable; pub mod request_hint; pub mod sequence; pub mod tag; -pub mod visitor; +pub mod value; diff --git a/tests/common/protocol/hint.rs b/tests/common/protocol/hint.rs index ec94277..37606bc 100644 --- a/tests/common/protocol/hint.rs +++ b/tests/common/protocol/hint.rs @@ -14,7 +14,7 @@ pub type KnownFactory<P> = mock! { pub HintWalker<P: HintMeta> { - pub fn hint<'a, 'ctx>(&'a mut self, visitor: DynVisitor<'a, 'ctx>, hint: MetaHint<'a, 'ctx, P>) -> Flow; + pub fn hint<'a, 'b, 'c, 'ctx>(&'a mut self, visitor: DynVisitor<'b, 'ctx>, hint: MetaHint<'c, 'ctx, P>) -> Flow; pub fn known(&self) -> KnownFactory<P>; } @@ -28,11 +28,14 @@ any_trait! { } impl<'ctx, P: HintMeta> Hint<'ctx, P> for MockHintWalker<P> { - fn hint<'a>( - &'a mut self, - visitor: DynVisitor<'a, 'ctx>, - hint: MetaHint<'a, 'ctx, P>, - ) -> ErasedEffective<'a, Flow, P::Effect> { + fn hint<'this, 'visitor, 'hint, 'e>( + &'this mut self, + visitor: DynVisitor<'visitor, 'ctx>, + hint: MetaHint<'hint, 'ctx, P>, + ) -> ErasedEffective<'e, Flow, P::Effect> + where + 'ctx: 'this + 'visitor + 'hint + 'e, + { P::Effect::ready(self.hint(visitor, hint)) } diff --git a/tests/common/protocol/visitor.rs b/tests/common/protocol/value.rs index b0f2273..b0f2273 100644 --- a/tests/common/protocol/visitor.rs +++ b/tests/common/protocol/value.rs diff --git a/tests/common/walker.rs b/tests/common/walker.rs index 5ba0ef8..abe7256 100644 --- a/tests/common/walker.rs +++ b/tests/common/walker.rs @@ -15,13 +15,15 @@ mock! { } } -impl<Output: Send + Sync, Error: Send + Sync> WalkerTypes for MockWalker<Output, Error> { +impl<Output: Send + Sync, Error: Send + Sync + core::fmt::Debug> WalkerTypes + for MockWalker<Output, Error> +{ type Error = Error; type Output = Output; } -impl<'ctx, Output: Send + Sync, Error: Send + Sync, E: Effect> Walker<'ctx, E> +impl<'ctx, Output: Send + Sync, Error: Send + Sync + core::fmt::Debug, E: Effect> Walker<'ctx, E> for MockWalker<Output, Error> { fn walk<'a: 'c, 'c>( diff --git a/tests/protocol_visitor_value.rs b/tests/protocol_visitor_value.rs index f24dc5e..821b9f6 100644 --- a/tests/protocol_visitor_value.rs +++ b/tests/protocol_visitor_value.rs @@ -2,7 +2,7 @@ use std::any::TypeId; use common::protocol::{ hint::{KnownFactory, MockHintWalker}, - visitor::MockValueVisitor, + value::MockValueVisitor, }; use mockall::predicate::eq; use treaty::{ diff --git a/tests/protocol_walker_hint.rs b/tests/protocol_walker_hint.rs index 4928752..a09b1f3 100644 --- a/tests/protocol_walker_hint.rs +++ b/tests/protocol_walker_hint.rs @@ -9,7 +9,7 @@ use treaty::{ }, hkt::higher_ranked_type, protocol::{ - walker::hint::{self, Hint, HintMeta, HintProto, Meta, MetaKnown}, + walker::hint::{self, Hint, HintMeta, HintProto, Meta, MetaHint, MetaKnown}, DynVisitor, }, Flow, @@ -108,11 +108,14 @@ fn known_can_have_temp_mutable_borrow() { struct Walker<'ctx>(&'ctx mut String); impl<'ctx> Hint<'ctx, MyProtocol> for Walker<'ctx> { - fn hint<'a>( - &'a mut self, - _visitor: DynVisitor<'a, 'ctx>, - _hint: <MyProtocol as HintMeta>::Hint, - ) -> ErasedEffective<'a, Flow, Blocking> { + fn hint<'this, 'visitor, 'hint, 'e>( + &'this mut self, + visitor: DynVisitor<'visitor, 'ctx>, + hint: MetaHint<'hint, 'ctx, MyProtocol>, + ) -> ErasedEffective<'e, Flow, Blocking> + where + 'ctx: 'this + 'visitor + 'hint + 'e, + { unreachable!() } @@ -167,11 +170,14 @@ fn known_can_have_context_borrow() { struct Walker<'ctx>(&'ctx String); impl<'ctx> Hint<'ctx, MyProtocol> for Walker<'ctx> { - fn hint<'a>( - &'a mut self, - _visitor: DynVisitor<'a, 'ctx>, - _hint: <MyProtocol as HintMeta>::Hint, - ) -> ErasedEffective<'a, Flow, Blocking> { + fn hint<'this, 'visitor, 'hint, 'e>( + &'this mut self, + visitor: DynVisitor<'visitor, 'ctx>, + hint: MetaHint<'hint, 'ctx, MyProtocol>, + ) -> ErasedEffective<'e, Flow, Blocking> + where + 'ctx: 'this + 'visitor + 'hint + 'e, + { unreachable!() } diff --git a/tests/walker_struct.rs b/tests/walker_struct.rs index 381b6c1..293ea55 100644 --- a/tests/walker_struct.rs +++ b/tests/walker_struct.rs @@ -1,21 +1,21 @@ use mockall::predicate::eq; use treaty::{ - any::{BorrowedStatic, BorrowedStaticHrt, OwnedStatic, TypeNameId}, + any::{BorrowedStatic, BorrowedStaticHrt, OwnedStatic, StaticType, TypeNameId}, effect::{blocking::Blocking, Effect, Effective, ErasedEffective}, protocol::{ visitor::{tags, SequenceProto, TagConst, TagProto, ValueProto, VisitResult}, DynVisitor, }, - walkers::core::r#struct::{StaticType, StructTypeInfo, StructWalker}, + walkers::core::r#struct::{StructTypeInfo, StructWalker}, Builder, DefaultMode, Flow, Walker, }; use crate::common::{ - builder::MockBuilder, + builder::{EmptyError, MockBuilder}, protocol::{ sequence::{MockSequenceVisitor, SequenceScopeFactory}, tag::MockTagVisitor, - visitor::MockValueVisitor, + value::MockValueVisitor, }, }; @@ -96,7 +96,7 @@ fn sequence_of_field_values() { // The struct walker using the info we provided about the struct. let walker = StructWalker::<Info, _, DefaultMode, Blocking>::new(&value); - let mut builder = MockBuilder::<(), (), ()>::new(); + let mut builder = MockBuilder::<(), (), EmptyError>::new(); // Expect a visit on the sequence protocol for the struct fields. builder @@ -114,7 +114,7 @@ fn sequence_of_field_values() { // Get the first field value. { - let mut visitor = MockBuilder::<(), (), ()>::new(); + let mut visitor = MockBuilder::<(), (), EmptyError>::new(); // Expect a bool value for the field. visitor @@ -147,7 +147,7 @@ fn sequence_of_field_values() { // Get the second field value. { - let mut visitor = MockBuilder::<(), (), ()>::new(); + let mut visitor = MockBuilder::<(), (), EmptyError>::new(); // Expect a i32 value. visitor @@ -206,7 +206,7 @@ fn has_struct_tag() { // The struct walker using the info we provided about the struct. let walker = StructWalker::<Info, _, DefaultMode, Blocking>::new(&value); - let mut builder = MockBuilder::<(), (), ()>::new(); + let mut builder = MockBuilder::<(), (), EmptyError>::new(); let mut seq = mockall::Sequence::new(); @@ -256,7 +256,7 @@ fn has_map_backup_tag() { // The struct walker using the info we provided about the struct. let walker = StructWalker::<Info, _, DefaultMode, Blocking>::new(&value); - let mut builder = MockBuilder::<(), (), ()>::new(); + let mut builder = MockBuilder::<(), (), EmptyError>::new(); let mut seq = mockall::Sequence::new(); @@ -308,7 +308,7 @@ fn borrowed_value_directly() { // The struct walker using the info we provided about the struct. let walker = StructWalker::<Info, _, DefaultMode, Blocking>::new(&value); - let mut builder = MockBuilder::<(), (), ()>::new(); + let mut builder = MockBuilder::<(), (), EmptyError>::new(); let mut seq = mockall::Sequence::new(); |