reviewed the struct builder API
Konnor Andrews 2024-06-02
parent 2db92e3 · commit 3d73588
-rw-r--r--Cargo.lock1
-rw-r--r--Cargo.toml1
-rw-r--r--src/any.rs4
-rw-r--r--src/build.rs8
-rw-r--r--src/build/builders/core.rs20
-rw-r--r--src/build/builders/core/bool.rs12
-rw-r--r--src/build/builders/core/struct.rs488
-rw-r--r--src/effect.rs8
-rw-r--r--src/lib.rs34
-rw-r--r--src/macros/build.rs87
-rw-r--r--src/protocol.rs4
-rw-r--r--src/protocol/walker/hint.rs42
-rw-r--r--src/transform.rs78
-rw-r--r--src/walk.rs4
-rw-r--r--src/walk/walkers/core/struct.rs178
-rw-r--r--tests/builder_struct.rs148
-rw-r--r--tests/common/builder.rs21
-rw-r--r--tests/common/protocol.rs2
-rw-r--r--tests/common/protocol/hint.rs15
-rw-r--r--tests/common/protocol/value.rs (renamed from tests/common/protocol/visitor.rs)0
-rw-r--r--tests/common/walker.rs6
-rw-r--r--tests/protocol_visitor_value.rs2
-rw-r--r--tests/protocol_walker_hint.rs28
-rw-r--r--tests/walker_struct.rs20
24 files changed, 759 insertions, 452 deletions
diff --git a/Cargo.lock b/Cargo.lock
index a41bca5..7c73423 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -744,6 +744,7 @@ dependencies = [
"mockall",
"pin-project",
"proptest",
+ "serde",
"serde_json",
"tokio",
]
diff --git a/Cargo.toml b/Cargo.toml
index c6ad4a8..6e936d2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"]
diff --git a/src/any.rs b/src/any.rs
index 9dc5115..79fb84a 100644
--- a/src/any.rs
+++ b/src/any.rs
@@ -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,
diff --git a/src/lib.rs b/src/lib.rs
index c4b7528..49232de 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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();