-rw-r--r--src/build.rs9
-rw-r--r--src/build/builders/core/enum.rs11
-rw-r--r--src/build/builders/core/struct.rs12
-rw-r--r--src/build/builders/core/value.rs12
-rw-r--r--src/effect.rs52
-rw-r--r--src/macros/build.rs171
-rw-r--r--src/protocol.rs15
-rw-r--r--src/protocol/visitor/tag.rs4
-rw-r--r--src/walk/walkers/core/value.rs32
-rw-r--r--src/walk/walkers/serde/deserializer.rs91
-rw-r--r--tests/builder_enum.rs179
-rw-r--r--tests/builder_struct.rs13
-rw-r--r--tests/builder_value.rs8
-rw-r--r--tests/common/builder.rs11
-rw-r--r--tests/common/protocol/recoverable.rs31
-rw-r--r--tests/common/protocol/tag.rs25
-rw-r--r--tests/common/protocol/value.rs34
-rw-r--r--tests/serde_deserializer.rs19
-rw-r--r--tests/walker_struct.rs26
19 files changed, 531 insertions, 224 deletions
diff --git a/src/build.rs b/src/build.rs
index 372eac8..4dbf564 100644
--- a/src/build.rs
+++ b/src/build.rs
@@ -3,7 +3,7 @@ pub mod builders;
use crate::{
effect::{Effect, ErasedEffective},
- protocol::DynVisitor,
+ protocol::{AsVisitor, DynVisitor},
};
/// A buildable type.
@@ -35,7 +35,7 @@ pub trait BuilderTypes {
/// the builder with data from it's walk.
/// - Call [`Self::build()`] to finish building the value and get any errors
/// that happened during filling it with data.
-pub trait Builder<'ctx, E: Effect>: BuilderTypes + Sized + Send + Sync {
+pub trait Builder<'ctx, E: Effect>: AsVisitor<'ctx> + BuilderTypes + Sized + Send + Sync {
fn from_seed<'a>(seed: Self::Seed) -> ErasedEffective<'a, Self, E>
where
Self: 'a;
@@ -47,9 +47,4 @@ pub trait Builder<'ctx, E: Effect>: BuilderTypes + Sized + Send + Sync {
fn build<'a>(self) -> ErasedEffective<'a, Result<Self::Value, Self::Error>, E>
where
Self: 'a;
-
- /// Get the builder as a visitor that a walker can use.
- ///
- /// This is expected to just be `self`.
- fn as_visitor(&mut self) -> DynVisitor<'_, 'ctx>;
}
diff --git a/src/build/builders/core/enum.rs b/src/build/builders/core/enum.rs
index 4e29bc4..a2bcb3b 100644
--- a/src/build/builders/core/enum.rs
+++ b/src/build/builders/core/enum.rs
@@ -3,6 +3,7 @@ use core::fmt::{Debug, Display};
use crate::any::{OwnedStatic, TempBorrowedStatic, TempBorrowedStaticHrt};
use crate::effect::{EffectExt, Effective, EffectiveExt};
use crate::protocol::visitor::{DynRecoverableScope, Recoverable, RecoverableProto};
+use crate::protocol::AsVisitor;
use crate::{
any::{AnyTraitObject, TypeName, TypeNameId},
any_trait,
@@ -106,8 +107,16 @@ where
Inner::Value(value) => E::ready(value),
}
}
+}
- fn as_visitor(&mut self) -> DynVisitor<'_, 'ctx> {
+impl<'ctx, Info, Mode, E: Effect> AsVisitor<'ctx> for EnumBuilder<'ctx, Info, Mode, E>
+where
+ Info: EnumBuildInfo<'ctx, Mode, E>,
+{
+ fn as_visitor<'a>(&'a mut self) -> DynVisitor<'a, 'ctx>
+ where
+ 'ctx: 'a,
+ {
DynVisitor(self)
}
}
diff --git a/src/build/builders/core/struct.rs b/src/build/builders/core/struct.rs
index 6bc1ef1..3675f1c 100644
--- a/src/build/builders/core/struct.rs
+++ b/src/build/builders/core/struct.rs
@@ -14,7 +14,7 @@ use crate::{
ValueProto, VisitResult,
},
walker::hint::hint_protocol,
- DynVisitor, DynWalker,
+ AsVisitor, DynVisitor, DynWalker,
},
tri, Builder, BuilderTypes, DynWalkerObjSafe, Flow,
};
@@ -218,8 +218,16 @@ where
Inner::Value(value) => Ok(value).ready(),
}
}
+}
- fn as_visitor(&mut self) -> DynVisitor<'_, 'ctx> {
+impl<'ctx, Info, Mode: 'ctx, E: Effect> AsVisitor<'ctx> for StructBuilder<'ctx, Info, Mode, E>
+where
+ Info: StructTypeInfo<'ctx, Mode, E>,
+{
+ fn as_visitor<'a>(&'a mut self) -> DynVisitor<'a, 'ctx>
+ where
+ 'ctx: 'a,
+ {
DynVisitor(self)
}
}
diff --git a/src/build/builders/core/value.rs b/src/build/builders/core/value.rs
index 22fe995..dba20bd 100644
--- a/src/build/builders/core/value.rs
+++ b/src/build/builders/core/value.rs
@@ -14,7 +14,7 @@ use crate::{
EffectiveVisitExt as _, RequestHint, RequestHintProto, Value, ValueProto, VisitResult,
},
walker::hint::hint_protocol,
- DynVisitor, DynWalker,
+ AsVisitor, DynVisitor, DynWalker,
},
Flow,
};
@@ -77,8 +77,16 @@ where
_marker: Default::default(),
})
}
+}
- fn as_visitor(&mut self) -> DynVisitor<'_, 'ctx> {
+impl<'ctx, T: Ss + 'static, Clone, E: Effect> AsVisitor<'ctx> for ValueBuilder<T, Clone, E>
+where
+ Self: AnyTrait<'ctx>,
+{
+ fn as_visitor<'a>(&'a mut self) -> DynVisitor<'a, 'ctx>
+ where
+ 'ctx: 'a,
+ {
DynVisitor(self)
}
}
diff --git a/src/effect.rs b/src/effect.rs
index 0b1a76b..f39f777 100644
--- a/src/effect.rs
+++ b/src/effect.rs
@@ -210,6 +210,19 @@ impl<T, E> ShortCircuit for Result<T, E> {
}
}
+pub trait ResultErrorExt<E> {
+ fn into_error(self) -> E;
+}
+
+impl<E> ResultErrorExt<E> for Result<Never, E> {
+ fn into_error(self) -> E {
+ match self {
+ Ok(_) => unreachable!(),
+ Err(err) => err,
+ }
+ }
+}
+
#[macro_export]
macro_rules! tri {
($expr:expr $(,)?) => {
@@ -424,6 +437,45 @@ pub trait EffectiveExt<'lt>: Effective<'lt> {
)
}
+ fn or_else_update<'ctx, 'wrap, Ctx, U, F>(
+ self,
+ f: F,
+ ) -> ErasedEffective<'wrap, (Ctx, U), Self::Effect>
+ where
+ Self: Effective<'lt, Output = (Ctx, U)>,
+ F: 'wrap
+ + for<'temp> FnOnce(
+ &'temp mut Ctx,
+ U::Short,
+ ) -> ErasedEffective<'temp, U, Self::Effect, &'ctx ()>,
+ U: ShortCircuit,
+ U::Short: Ss,
+ Ctx: Ss,
+ U: Ss,
+ F: Ss,
+ 'ctx: 'lt,
+ 'lt: 'wrap,
+ {
+ self.r#do(
+ |(ctx, v)| {
+ (
+ ctx,
+ // The inverting of the control flow is because on breaks we want to run the
+ // function not skip it.
+ match U::branch(v) {
+ ControlFlow::Continue(v) => ControlFlow::Break(U::from_output(v)),
+ ControlFlow::Break(v) => ControlFlow::Continue(v),
+ },
+ )
+ },
+ |ctx, v| f(ctx, v).cast(),
+ |_, v| ControlFlow::<_, Never>::Break(v),
+ |_, _| unreachable!(),
+ |_, _, _: Never| unreachable!(),
+ |ctx, _, v| (ctx, v),
+ )
+ }
+
#[allow(clippy::wrong_self_convention)]
fn as_ctx_or_else_map<'ctx, 'wrap, Ctx, U, F>(
self,
diff --git a/src/macros/build.rs b/src/macros/build.rs
index 7eb3fbe..b02c123 100644
--- a/src/macros/build.rs
+++ b/src/macros/build.rs
@@ -107,7 +107,7 @@ macro_rules! Build {
marker: Self::FieldMarker,
builders: &'a mut Self::Builders,
) -> $crate::protocol::DynVisitor<'a, 'ctx> {
- use $crate::Builder;
+ use $crate::protocol::AsVisitor;
match marker {
$(Field::$field => builders.$field.as_visitor()),*
@@ -133,5 +133,172 @@ macro_rules! Build {
}
}
};
- }
+ };
+ {
+ $(#[$($attr:tt)*])*
+ $vis:vis enum $name:ident {$(
+ $variant:ident($value:ty)
+ ),* $(,)?}
+ } => {
+ #[allow(non_upper_case_globals, non_snake_case, non_camel_case_types)]
+ const _: () = {
+ // add a module here to seal fields.
+ impl<'ctx, M: 'ctx, E: $crate::effect::Effect> $crate::Build<'ctx, M, E> for $name {
+ type Builder = $crate::builders::core::r#enum::EnumBuilder<'ctx, __Info, M, E>;
+ }
+
+ $vis struct __Info;
+
+ $vis enum __Builders<'ctx, M, E: Effect> {
+ $($variant(<$value as $crate::Build<'ctx, M, E>>::Builder)),*
+ }
+
+ #[derive(Copy, Clone)]
+ $vis enum __Marker {
+ $($variant),*
+ }
+
+ impl ::core::fmt::Display for __Marker {
+ fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
+ f.write_str(match self {
+ $(Self::$variant => stringify!($variant)),*
+ })
+ }
+ }
+
+ mod variant_index {
+ enum __Variants {
+ $($variant),*
+ }
+
+ $(pub const $variant: u32 = __Variants::$variant as u32;)*
+ }
+
+ $vis struct __ErrorBuilder<'ctx, M, E: Effect> {
+ $($variant: Option<<<$value as $crate::Build<'ctx, M, E>>::Builder as BuilderTypes>::Error>),*
+ }
+
+ $vis enum __Error<'ctx, M, E: Effect> {
+ __Guess {
+ $($variant: <<$value as $crate::Build<'ctx, M, E>>::Builder as BuilderTypes>::Error),*
+ },
+ $($variant(<<$value as $crate::Build<'ctx, M, E>>::Builder as BuilderTypes>::Error)),*
+ }
+
+ impl<'ctx, M, E: Effect> ::core::fmt::Display for __Error<'ctx, M, E> {
+ fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
+ match self {
+ Self::__Guess {
+ $($variant),*
+ } => {
+ $(writeln!(f, "{}: {}", stringify!($variant), $variant)?;)*
+
+ Ok(())
+ }
+ $(Self::$variant(value) => write!(f, "{}: {}", stringify!($variant), value)),*
+ }
+ }
+ }
+
+ impl<'ctx, M, E: Effect> ::core::fmt::Debug for __Error<'ctx, M, E> {
+ fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
+ match self {
+ Self::__Guess {
+ $($variant),*
+ } => {
+ $(writeln!(f, "{}: {:?}", stringify!($variant), $variant)?;)*
+
+ Ok(())
+ }
+ $(Self::$variant(value) => write!(f, "{}: {:?}", stringify!($variant), value)),*
+ }
+ }
+ }
+
+ impl<'ctx, M: 'ctx, E: $crate::effect::Effect> $crate::builders::core::r#enum::EnumBuildInfo<'ctx, M, E> for __Info {
+ type Builders = __Builders<'ctx, M, E>;
+
+ type Seed = ();
+
+ type Error = __Error<'ctx, M, E>;
+
+ type ValueT = $crate::any::OwnedStatic<$name>;
+
+ type T = $name;
+
+ type VariantMarker = __Marker;
+
+ fn new_builder<'a>(
+ seed: Self::Seed,
+ variant: Self::VariantMarker,
+ ) -> ErasedEffective<'a, Self::Builders, E> {
+ match variant {
+ $(__Marker::$variant => {
+ Builder::<E>::from_seed(Default::default()).map(|builder| __Builders::$variant(builder))
+ })*
+ }
+ }
+
+ fn finish_builder<'a>(
+ builder: Self::Builders,
+ ) -> ErasedEffective<'a, Result<Self::T, Self::Error>, E> {
+ match builder {
+ $(__Builders::$variant(builder) => builder.build().map(|value| value.map($name::$variant).map_err(__Error::$variant))),*
+ }
+ }
+
+ fn from_value<'a>(value: TypeName::T<'a, 'ctx, Self::ValueT>) -> Self::T {
+ value.0
+ }
+
+ fn as_visitor<'a>(builder: &'a mut Self::Builders) -> DynVisitor<'a, 'ctx> {
+ match builder {
+ $(__Builders::$variant(builder) => builder.as_visitor()),*
+ }
+ }
+
+ fn marker_from_name(name: &str) -> Option<Self::VariantMarker> {
+ match name {
+ $(stringify!($variant) => Some(__Marker::$variant),)*
+ _ => None
+ }
+ }
+
+ fn marker_from_discriminant(discriminant: u32) -> Option<Self::VariantMarker> {
+ match discriminant {
+ $(variant_index::$variant => Some(__Marker::$variant),)*
+ _ => None
+ }
+ }
+
+ fn guess_variant<'a>(
+ seed: Self::Seed,
+ scope: DynRecoverableScope<'a, 'ctx, E>,
+ ) -> ErasedEffective<'a, Result<Self::T, Self::Error>, E> {
+ E::ready((scope, Err(__ErrorBuilder::<M, E> { A: None, B: None })))
+ $(.or_else_update(|scope, result| {
+ let mut error = result.into_error();
+
+ <<$value as Build<M, E>>::Builder as Builder<_>>::from_seed(Default::default())
+ .map(|builder| (scope, builder))
+ .as_ctx(|(scope, builder)| scope.new_walk(builder.as_visitor()).cast())
+ .then(|((_, builder), _)| builder.build())
+ .map(|result| {
+ result.map(X::$variant).map_err(|err| {
+ error.$variant = Some(err);
+ error
+ })
+ })
+ .cast()
+ }))*
+ .map(|(_, result)| match result {
+ Ok(value) => Ok(value),
+ Err(err) => Err(__Error::__Guess {
+ $($variant: err.$variant.unwrap()),*
+ }),
+ })
+ }
+ }
+ };
+ };
}
diff --git a/src/protocol.rs b/src/protocol.rs
index 60db903..238bbab 100644
--- a/src/protocol.rs
+++ b/src/protocol.rs
@@ -81,6 +81,21 @@ impl<'a, 'ctx> DerefMut for DynVisitor<'a, 'ctx> {
}
}
+pub trait AsVisitor<'ctx> {
+ fn as_visitor<'a>(&'a mut self) -> DynVisitor<'a, 'ctx>
+ where
+ 'ctx: 'a;
+}
+
+impl<'b, 'ctx> AsVisitor<'ctx> for DynVisitor<'b, 'ctx> {
+ fn as_visitor<'a>(&'a mut self) -> DynVisitor<'a, 'ctx>
+ where
+ 'ctx: 'a,
+ {
+ self.cast()
+ }
+}
+
pub struct DynWalker<'a, 'ctx>(pub &'a mut (dyn AnyTrait<'ctx> + Send + Sync + 'a));
impl<'a, 'ctx> DynWalker<'a, 'ctx> {
diff --git a/src/protocol/visitor/tag.rs b/src/protocol/visitor/tag.rs
index ffc692a..802da60 100644
--- a/src/protocol/visitor/tag.rs
+++ b/src/protocol/visitor/tag.rs
@@ -43,6 +43,10 @@ impl<const SYMBOL: u64> TagKind for TagConst<SYMBOL> {
}
}
+impl<const SYMBOL: u64> TagConst<SYMBOL> {
+ pub const VALUE: u64 = SYMBOL;
+}
+
impl TagKind for TagDyn {
fn symbol(&self) -> Symbol {
self.0
diff --git a/src/walk/walkers/core/value.rs b/src/walk/walkers/core/value.rs
index ce67f88..252ca35 100644
--- a/src/walk/walkers/core/value.rs
+++ b/src/walk/walkers/core/value.rs
@@ -1,8 +1,11 @@
use crate::{
- any::OwnedStatic,
- effect::{Effect, EffectiveExt as _, ErasedEffective},
+ any::{BorrowedStatic, OwnedStatic, TempBorrowedStatic},
+ effect::{Effect, EffectExt as _, Effective, EffectiveExt as _, ErasedEffective},
never::Never,
- protocol::{visitor::visit_value, DynVisitor},
+ protocol::{
+ visitor::{visit_value, EffectiveVisitExt as _, VisitResult},
+ DynVisitor,
+ },
};
/// A very basic walker that uses the [`Value`][crate::protocol::visitor::value::Value] protocol.
@@ -46,10 +49,6 @@ impl<'ctx, T: Send + Sync + 'static, E: Effect> crate::Walker<'ctx, E> for Value
) -> ErasedEffective<'c, Result<Self::Output, Self::Error>, E> {
// Attempt to visit using the value protocol.
visit_value::<_, E>(visitor, OwnedStatic(self.0)).map(|_| Ok(()))
- // E::map(
- // visit_value::<_, E>(visitor, OwnedStatic(self.0)),
- // |_| Ok(()),
- // )
}
}
@@ -71,17 +70,24 @@ impl<'ctx, T: ?Sized + Send + Sync + 'static, E: Effect> crate::Walker<'ctx, E>
{
type Error = Never;
- type Output = ();
+ type Output = &'ctx T;
#[inline(always)]
fn walk<'b: 'c, 'c>(
self,
- _visitor: DynVisitor<'b, 'ctx>,
+ visitor: DynVisitor<'b, 'ctx>,
) -> ErasedEffective<'c, Result<Self::Output, Self::Error>, E> {
// Attempt to visit using the value protocol.
- todo!()
- // E::map(visit_value::<_, E>(visitor, BorrowedStatic(self.0)), |_| {
- // Ok(())
- // })
+ E::as_ctx((self, visitor), |(this, visitor)| {
+ visit_value::<_, E>(visitor.cast(), BorrowedStatic(this.0))
+ .map(VisitResult::unit_skipped)
+ .cast()
+ })
+ .if_skipped(|(this, visitor)| {
+ visit_value::<_, E>(visitor.cast(), TempBorrowedStatic(this.0))
+ .map(VisitResult::unit_skipped)
+ .cast()
+ })
+ .map(|((this, _), _)| Ok(this.0))
}
}
diff --git a/src/walk/walkers/serde/deserializer.rs b/src/walk/walkers/serde/deserializer.rs
index 5a481e0..70050d5 100644
--- a/src/walk/walkers/serde/deserializer.rs
+++ b/src/walk/walkers/serde/deserializer.rs
@@ -1,14 +1,17 @@
-use serde::Deserializer;
+use serde::{de::MapAccess, Deserializer};
use crate::{
any::{BorrowedStaticHrt, OwnedStatic, TempBorrowedStatic, TempBorrowedStaticHrt, TypeName},
any_trait,
effect::{
- BlockOn, Effect, EffectExt as _, Effective as _, EffectiveExt as _, ErasedEffective, Ss,
+ BlockOn, Effect, EffectExt as _, Effective as _, EffectiveExt as _, ErasedEffective,
+ ReadyExt as _, Ss,
},
hkt::Marker,
protocol::{
- visitor::{request_hint, visit_value, ValueKnown, ValueProto, VisitResult},
+ visitor::{
+ request_hint, tags, visit_sequence, visit_value, EffectiveVisitExt as _, SequenceScope, TagConst, TagKnown, TagProto, ValueKnown, ValueProto, VisitResult
+ },
walker::hint::{DynVisitorWith, Hint, HintMeta, HintProto, MetaHint, MetaKnown},
DynVisitor, DynWalker,
},
@@ -108,6 +111,12 @@ where
.map(VisitResult::unit_skipped)
.cast()
})
+ .if_not_finished(|(this, visitor)| {
+ this.call_deserialize(|deserializer| {
+ deserializer.deserialize_any(Visitor::<T, E>::new(visitor.cast(), "any"))
+ })
+ .cast()
+ })
.map(|((this, _), _)| match this.inner {
Inner::Temp => todo!(),
Inner::Init(_) => todo!(),
@@ -131,6 +140,7 @@ any_trait! {
HintProto<ValueProto<OwnedStatic<u64>, E>>,
HintProto<ValueProto<OwnedStatic<u128>, E>>,
HintProto<ValueProto<OwnedStatic<char>, E>>,
+ HintProto<TagProto<tags::Map, E>>,
] where
T: Deserializer<'ctx> + Ss + 'ctx,
T::Error: Ss,
@@ -201,7 +211,7 @@ macro_rules! impl_hints {
where
$ctx: 'this + 'visitor + 'hint + 'e,
{
- self.call_deserialize(|deserializer| deserializer.$method(Visitor::new(visitor, stringify!($type))))
+ self.call_deserialize(|deserializer| deserializer.$method(Visitor::<$T, $E>::new(visitor.into_inner(), stringify!($type))))
}
fn known<'a>(
@@ -239,6 +249,40 @@ impl_hints! {
// ...
}
+impl<'ctx, T, E: Effect> Hint<'ctx, TagProto<tags::Map, E>> for DeserializerWalker<'ctx, T, E>
+where
+ T: Deserializer<'ctx> + Ss + 'ctx,
+ T::Error: Ss,
+{
+ fn hint<'this: 'e, 'visitor: 'e, 'hint: 'e, 'e>(
+ &'this mut self,
+ visitor: DynVisitorWith<'visitor, 'ctx, TagProto<tags::Map, E>>,
+ hint: MetaHint<'hint, 'ctx, TagProto<tags::Map, E>>,
+ ) -> ErasedEffective<'e, VisitResult, <TagProto<tags::Map, E> as HintMeta>::Effect>
+ where
+ 'ctx: 'this + 'visitor + 'hint + 'e,
+ {
+ // self.call_deserialize(|deserializer| {
+ // todo!()
+ // })
+ VisitResult::Skipped(()).ready()
+ }
+
+ fn known<'a>(
+ &'a mut self,
+ _hint: &'a MetaHint<'a, 'ctx, TagProto<tags::Map, E>>,
+ ) -> ErasedEffective<
+ 'a,
+ Result<MetaKnown<'a, 'ctx, TagProto<tags::Map, E>>, ()>,
+ <TagProto<tags::Map, E> as HintMeta>::Effect,
+ > {
+ Ok(TagKnown {
+ kind_available: Some(true),
+ })
+ .ready()
+ }
+}
+
#[allow(non_camel_case_types, unused)]
enum VisitorError<'ctx, T>
where
@@ -272,13 +316,10 @@ impl<'temp, 'ctx, T, E> Visitor<'temp, 'ctx, T, E>
where
T: Deserializer<'ctx>,
{
- pub fn new<Protocol: HintMeta<Effect = E>>(
- visitor: DynVisitorWith<'temp, 'ctx, Protocol>,
- wanted: &'static str,
- ) -> Self {
+ pub fn new(visitor: DynVisitor<'temp, 'ctx>, wanted: &'static str) -> Self {
Self {
wanted,
- visitor: visitor.into_inner(),
+ visitor,
_marker: Default::default(),
}
}
@@ -343,4 +384,36 @@ where
visit_u64: u64,
visit_u128: u128,
}
+
+ fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
+ where
+ A: MapAccess<'ctx>,
+ {
+ let mut scope = MapScope { map };
+ visit_sequence(self.visitor, &mut scope);
+
+ todo!()
+ }
+}
+
+struct MapScope<A> {
+ map: A
+}
+
+impl<'ctx, A, E: Effect> SequenceScope<'ctx, E> for MapScope<A>
+where
+ A: MapAccess<'ctx>
+{
+ fn size_hint(&mut self) -> ErasedEffective<'_, (usize, Option<usize>), E> {
+ (0, self.map.size_hint()).ready()
+ }
+
+ fn next<'a: 'c, 'b: 'c, 'c>(
+ &'a mut self,
+ visitor: DynVisitor<'b, 'ctx>,
+ ) -> ErasedEffective<'c, Flow, E>
+ where
+ 'ctx: 'c + 'a + 'b {
+ todo!()
+ }
}
diff --git a/tests/builder_enum.rs b/tests/builder_enum.rs
index dbdc60f..b589d6c 100644
--- a/tests/builder_enum.rs
+++ b/tests/builder_enum.rs
@@ -1,167 +1,67 @@
use treaty::{
any::{OwnedStatic, TypeName},
- builders::core::r#enum::{EnumBuildInfo, EnumBuilder},
effect::{
- blocking::Blocking, Effect, EffectExt as _, Effective as _, EffectiveExt as _,
- ErasedEffective,
+ blocking::Blocking, Effect, Effective as _, EffectiveExt as _, ErasedEffective,
+ ResultErrorExt as _,
},
protocol::{
- visitor::{tags, visit_recoverable, visit_tag, visit_value, DynRecoverableScope, TagConst},
- DynVisitor,
+ visitor::{tags, DynRecoverableScope},
+ AsVisitor as _, DynVisitor,
},
- walkers::core::value::ValueWalker,
- Build, BuildExt as _, Builder, Flow, Status, WalkExt as _,
+ walkers::core::value::{BorrowWalker, ValueWalker},
+ Build, BuildExt as _, Builder, BuilderTypes, Status,
};
use crate::common::protocol::{
- recoverable::MockRecoverableScopeVisitor, value::ValueVisitorExt as _,
+ recoverable::{MockRecoverableScopeVisitor, RecoverableVisitorExt as _},
+ tag::TagVisitorExt as _,
+ value::ValueVisitorExt as _,
};
+use macro_rules_attribute::derive;
+
mod common;
-#[derive(PartialEq, Debug)]
+#[derive(Build!, PartialEq, Debug)]
enum X {
A(f32),
B(bool),
}
-impl<'ctx, M, E: Effect> Build<'ctx, M, E> for X {
- type Builder = EnumBuilder<'ctx, Info, M, E>;
-}
-
-enum Info {}
-
-enum XBuilders<'ctx, M, E: Effect> {
- A(<f32 as Build<'ctx, M, E>>::Builder),
- B(<bool as Build<'ctx, M, E>>::Builder),
-}
-
-#[derive(Copy, Clone, Debug)]
-enum XMarker {
- A,
- B,
-}
-
-impl core::fmt::Display for XMarker {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- todo!()
- }
-}
-
-impl<'ctx, M, E: Effect> EnumBuildInfo<'ctx, M, E> for Info {
- type Builders = XBuilders<'ctx, M, E>;
-
- type Seed = ();
-
- type Error = i32;
-
- type ValueT = OwnedStatic<X>;
-
- type T = X;
-
- type VariantMarker = XMarker;
-
- fn new_builder<'a>(
- _seed: Self::Seed,
- variant: Self::VariantMarker,
- ) -> ErasedEffective<'a, Self::Builders, E> {
- match variant {
- XMarker::A => {
- Builder::<E>::from_seed(Default::default()).map(|builder| XBuilders::A(builder))
- }
- XMarker::B => {
- Builder::<E>::from_seed(Default::default()).map(|builder| XBuilders::B(builder))
- }
- }
- }
-
- fn from_value<'a>(value: TypeName::T<'a, 'ctx, Self::ValueT>) -> Self::T {
- value.0
- }
-
- fn as_visitor<'a>(builder: &'a mut Self::Builders) -> DynVisitor<'a, 'ctx> {
- match builder {
- XBuilders::A(builder) => builder.as_visitor(),
- XBuilders::B(builder) => builder.as_visitor(),
- }
- }
-
- fn marker_from_name(name: &str) -> Option<Self::VariantMarker> {
- match name {
- "A" => Some(XMarker::A),
- "B" => Some(XMarker::B),
- _ => None,
- }
- }
+#[test]
+fn enum_builder_takes_unsigned_integer_variant_tag() {
+ let mut builder = X::new_builder();
- fn marker_from_discriminant(discriminant: u32) -> Option<Self::VariantMarker> {
- match discriminant {
- 0 => Some(XMarker::A),
- 1 => Some(XMarker::B),
- _ => None,
- }
- }
+ // Use the int variant then value flow.
+ {
+ // Signal the first variant.
+ builder.visit_tag_and_done::<{ tags::Variant::VALUE }, _>(ValueWalker::new(0u32));
- fn finish_builder<'a>(
- builder: Self::Builders,
- ) -> ErasedEffective<'a, Result<Self::T, Self::Error>, E> {
- match builder {
- XBuilders::A(builder) => builder.build().map(|value| Ok(X::A(value.unwrap()))),
- XBuilders::B(builder) => builder.build().map(|value| Ok(X::B(value.unwrap()))),
- }
+ // Give the value for the variant.
+ builder.visit_value_and_done(OwnedStatic(1.23f32));
}
- fn guess_variant<'a>(
- _seed: Self::Seed,
- scope: DynRecoverableScope<'a, 'ctx, E>,
- ) -> ErasedEffective<'a, Result<Self::T, Self::Error>, E> {
- E::as_ctx(scope, |scope| {
- <<f32 as Build<M, E>>::Builder as Builder<_>>::from_seed(Default::default())
- .map(|builder| (scope, builder))
- .as_ctx(|(scope, builder)| scope.new_walk(builder.as_visitor()).cast())
- .then(|((_, builder), result)| builder.build())
- .map(|result| result.map(X::A))
- .cast()
- })
- .as_ctx(|(scope, result)| {
- <<bool as Build<M, E>>::Builder as Builder<_>>::from_seed(Default::default())
- .map(|builder| (scope, builder))
- .as_ctx(|(scope, builder)| scope.new_walk(builder.as_visitor()).cast())
- .then(|((_, builder), result)| builder.build())
- .map(|result| result.map(X::B))
- .cast()
- })
- .map(|((scope, _), result)| match result {
- Ok(value) => Ok(value),
- Err(err) => todo!("{}", err),
- })
- }
+ assert_eq!(builder.build().value().unwrap(), X::A(1.23));
}
#[test]
-fn demo() {
+fn enum_builder_takes_string_variant_tag() {
let mut builder = X::new_builder();
- assert_eq!(
- visit_tag::<tags::Variant, Blocking, _>(
- TagConst,
- builder.as_visitor(),
- ValueWalker::new(0u32),
- )
- .value()
- .unwrap(),
- Flow::Done.into()
- );
-
- builder
- .as_visitor()
- .visit_value_and_done(OwnedStatic(1.23f32));
+ // Use the string variant then value flow.
+ {
+ // Signal the A variant.
+ builder.visit_tag_and_done::<{ tags::Variant::VALUE }, _>(BorrowWalker::new("A"));
+
+ // Give the value for the variant.
+ builder.visit_value_and_done(OwnedStatic(1.23f32));
+ }
assert_eq!(builder.build().value().unwrap(), X::A(1.23));
}
#[test]
-fn demo2() {
+fn enum_builder_can_guess_the_variant() {
let mut builder = X::new_builder();
// Use the recoverable flow.
@@ -169,11 +69,15 @@ fn demo2() {
let mut scope = MockRecoverableScopeVisitor::<Blocking>::new();
// The first builder for a f32 won't work.
- // In a real walker the scope would always walk the same.
- scope.expect_new_walk().once().return_const(Status::Ok);
+ scope.expect_new_walk().once().returning(|mut visitor| {
+ // The value for the B variant.
+ visitor.visit_value_and_skipped(OwnedStatic(true));
+
+ Status::Ok
+ });
// The second builder will work.
- scope.expect_new_walk().once().returning(|visitor| {
+ scope.expect_new_walk().once().returning(|mut visitor| {
// The value for the B variant.
visitor.visit_value_and_done(OwnedStatic(true));
@@ -182,10 +86,7 @@ fn demo2() {
// Visit a recoverable scope the enum builder can use to try and find
// a variant that can be built.
- assert_eq!(
- visit_recoverable(builder.as_visitor(), &mut scope).value(),
- Flow::Done.into()
- );
+ builder.visit_recoverable_and_done(&mut scope);
}
// The enum should have a value now.
diff --git a/tests/builder_struct.rs b/tests/builder_struct.rs
index 8dc9949..b758379 100644
--- a/tests/builder_struct.rs
+++ b/tests/builder_struct.rs
@@ -2,7 +2,10 @@ use macro_rules_attribute::derive;
use treaty::{
any::{OwnedStatic, TempBorrowedStatic},
effect::blocking::Blocking,
- protocol::visitor::{tags, visit_sequence, visit_tag, TagConst, VisitResult},
+ protocol::{
+ visitor::{tags, visit_sequence, visit_tag, TagConst, VisitResult},
+ AsVisitor as _,
+ },
walkers::core::noop::NoopWalker,
Build, BuildExt as _, Builder, Flow,
};
@@ -28,7 +31,7 @@ fn a_struct_builder_can_build_from_a_sequence_of_field_values() {
scope = MockSequenceScope::<Blocking>::new();
// First field.
- scope.expect_next().once().returning(|visitor| {
+ scope.expect_next().once().returning(|mut visitor| {
// Visit a bool value.
visitor.visit_value_and_done(OwnedStatic(true));
@@ -37,7 +40,7 @@ fn a_struct_builder_can_build_from_a_sequence_of_field_values() {
});
// Second field.
- scope.expect_next().once().returning(|visitor| {
+ scope.expect_next().once().returning(|mut visitor| {
// Visit a bool value.
visitor.visit_value_and_done(OwnedStatic(false));
@@ -74,7 +77,7 @@ fn a_struct_builder_can_build_from_a_sequence_of_keyed_values() {
walker = MockWalker::<(), ()>::new();
// We need to give the b field name in the key tag.
- walker.expect_walk().once().returning(|visitor| {
+ walker.expect_walk().once().returning(|mut visitor| {
visitor.visit_value_and_done(TempBorrowedStatic("b"));
Ok(())
@@ -101,7 +104,7 @@ fn a_struct_builder_can_build_from_a_sequence_of_keyed_values() {
walker = MockWalker::<(), ()>::new();
// Here we do field a.
- walker.expect_walk().once().returning(|visitor| {
+ walker.expect_walk().once().returning(|mut visitor| {
visitor.visit_value_and_done(TempBorrowedStatic("a"));
Ok(())
diff --git a/tests/builder_value.rs b/tests/builder_value.rs
index b1d36de..7e59b76 100644
--- a/tests/builder_value.rs
+++ b/tests/builder_value.rs
@@ -6,7 +6,7 @@ use treaty::{
effect::blocking::Blocking,
protocol::{
visitor::{request_hint, ValueProto},
- DynWalker,
+ AsVisitor as _, DynWalker,
},
BuildExt as _, Builder as _, Flow,
};
@@ -46,7 +46,7 @@ fn value_builder_can_use_an_owned_value_or_a_borrowed_value() {
assert_eq!(
i32::build({
let mut walker = MockWalker::<(), ()>::new();
- walker.expect_walk().once().returning(|visitor| {
+ walker.expect_walk().once().returning(|mut visitor| {
visitor.visit_value_and_done(OwnedStatic(1));
Ok(())
});
@@ -59,7 +59,7 @@ fn value_builder_can_use_an_owned_value_or_a_borrowed_value() {
assert_eq!(
i32::build({
let mut walker = MockWalker::<(), ()>::new();
- walker.expect_walk().once().returning(|visitor| {
+ walker.expect_walk().once().returning(|mut visitor| {
visitor.visit_value_and_done(BorrowedStatic(&2));
Ok(())
});
@@ -72,7 +72,7 @@ fn value_builder_can_use_an_owned_value_or_a_borrowed_value() {
assert_eq!(
i32::build({
let mut walker = MockWalker::<(), ()>::new();
- walker.expect_walk().once().returning(|visitor| {
+ walker.expect_walk().once().returning(|mut visitor| {
visitor.visit_value_and_done(TempBorrowedStatic(&3));
Ok(())
});
diff --git a/tests/common/builder.rs b/tests/common/builder.rs
index d5a9ec3..74b78cd 100644
--- a/tests/common/builder.rs
+++ b/tests/common/builder.rs
@@ -3,7 +3,7 @@ use mockall::mock;
use treaty::{
any::{indirect, AnyTrait, AnyTraitObject, TypeNameId},
effect::{Effect, Effective, ErasedEffective},
- protocol::DynVisitor,
+ protocol::{AsVisitor, DynVisitor},
Builder, BuilderTypes,
};
@@ -77,8 +77,15 @@ impl<
{
E::ready(self.build())
}
+}
- fn as_visitor(&mut self) -> DynVisitor<'_, 'ctx> {
+impl<'ctx, Seed: Send + Sync, Value: Send + Sync, Error: Send + Sync + Display + Debug>
+ AsVisitor<'ctx> for MockBuilder<Seed, Value, Error>
+{
+ fn as_visitor<'a>(&'a mut self) -> DynVisitor<'a, 'ctx>
+ where
+ 'ctx: 'a,
+ {
DynVisitor(self)
}
}
diff --git a/tests/common/protocol/recoverable.rs b/tests/common/protocol/recoverable.rs
index c338b09..378e215 100644
--- a/tests/common/protocol/recoverable.rs
+++ b/tests/common/protocol/recoverable.rs
@@ -1,17 +1,14 @@
use mockall::mock;
use treaty::{
any::{any_trait, TypeName},
- effect::{Effect, Effective, ErasedEffective},
- protocol::{
- visitor::{DynRecoverableScope, Recoverable, RecoverableProto, RecoverableScope},
- DynWalker,
- },
+ effect::{blocking::Blocking, Effect, Effective, ErasedEffective},
protocol::{
visitor::{
- DynSequenceScope, RequestHint, RequestHintProto, Sequence, SequenceProto,
- SequenceScope, Value, ValueProto, VisitResult,
+ visit_recoverable, visit_sequence, DynRecoverableScope, DynSequenceScope, Recoverable,
+ RecoverableProto, RecoverableScope, RequestHint, RequestHintProto, Sequence,
+ SequenceProto, SequenceScope, Value, ValueProto, VisitResult,
},
- DynVisitor,
+ AsVisitor, DynVisitor, DynWalker,
},
Flow, Status,
};
@@ -52,3 +49,21 @@ impl<'ctx, E: Effect> RecoverableScope<'ctx, E> for MockRecoverableScopeVisitor<
E::ready(self.new_walk(visitor))
}
}
+
+pub trait RecoverableVisitorExt<'ctx> {
+ fn visit_recoverable_and_done<'a>(&'a mut self, scope: DynRecoverableScope<'a, 'ctx, Blocking>);
+}
+
+impl<'ctx, T> RecoverableVisitorExt<'ctx> for T
+where
+ T: AsVisitor<'ctx>,
+{
+ fn visit_recoverable_and_done<'a>(
+ &'a mut self,
+ scope: DynRecoverableScope<'a, 'ctx, Blocking>,
+ ) {
+ let result = visit_recoverable(self.as_visitor(), scope).value();
+
+ assert_eq!(result, VisitResult::Control(Flow::Done));
+ }
+}
diff --git a/tests/common/protocol/tag.rs b/tests/common/protocol/tag.rs
index 890157e..db11438 100644
--- a/tests/common/protocol/tag.rs
+++ b/tests/common/protocol/tag.rs
@@ -1,9 +1,12 @@
use mockall::mock;
use treaty::{
any::any_trait,
- effect::{Effect, Effective, ErasedEffective},
- protocol::visitor::{Tag, TagKind, TagProto, VisitResult},
- DynWalkerObjSafe,
+ effect::{blocking::Blocking, Effect, Effective, ErasedEffective},
+ protocol::{
+ visitor::{visit_tag, Tag, TagConst, TagKind, TagProto, VisitResult},
+ AsVisitor, DynVisitor,
+ },
+ DynWalkerObjSafe, Flow, Walker,
};
mock! {
@@ -29,3 +32,19 @@ impl<'ctx, K: TagKind, E: Effect> Tag<'ctx, K, E> for MockTagVisitor<K, E> {
E::ready(self.visit(kind, walker))
}
}
+
+pub trait TagVisitorExt<'ctx> {
+ fn visit_tag_and_done<'a, const TAG: u64, W: Walker<'ctx, Blocking>>(&'a mut self, walker: W);
+}
+
+impl<'ctx, T> TagVisitorExt<'ctx> for T
+where
+ T: AsVisitor<'ctx>,
+{
+ fn visit_tag_and_done<'a, const TAG: u64, W: Walker<'ctx, Blocking>>(&'a mut self, walker: W) {
+ let result =
+ visit_tag::<TagConst<TAG>, Blocking, _>(TagConst, self.as_visitor(), walker).value();
+
+ assert_eq!(result.unwrap(), VisitResult::Control(Flow::Done));
+ }
+}
diff --git a/tests/common/protocol/value.rs b/tests/common/protocol/value.rs
index 690d3d1..898b21d 100644
--- a/tests/common/protocol/value.rs
+++ b/tests/common/protocol/value.rs
@@ -4,7 +4,7 @@ use treaty::{
effect::{blocking::Blocking, Effect, Effective, ErasedEffective, Ss},
protocol::{
visitor::{visit_value, Value, ValueProto, VisitResult},
- DynVisitor,
+ AsVisitor, DynVisitor,
},
Flow,
};
@@ -46,19 +46,39 @@ where
}
}
-pub trait ValueVisitorExt<'a, 'ctx: 'a> {
- fn visit_value_and_done<T>(self, value: T)
+pub trait ValueVisitorExt<'ctx> {
+ fn visit_value_and_done<'a, T>(&'a mut self, value: T)
where
- T: TypeName::LowerType<'a, 'ctx>;
+ T: TypeName::LowerType<'a, 'ctx>,
+ 'ctx: 'a;
+
+ fn visit_value_and_skipped<'a, T>(&'a mut self, value: T)
+ where
+ T: TypeName::LowerType<'a, 'ctx>,
+ 'ctx: 'a;
}
-impl<'a, 'ctx: 'a> ValueVisitorExt<'a, 'ctx> for DynVisitor<'a, 'ctx> {
- fn visit_value_and_done<T>(self, value: T)
+impl<'ctx, U> ValueVisitorExt<'ctx> for U
+where
+ U: AsVisitor<'ctx>,
+{
+ fn visit_value_and_done<'a, T>(&'a mut self, value: T)
where
T: TypeName::LowerType<'a, 'ctx>,
+ 'ctx: 'a,
{
- let result = visit_value::<_, Blocking>(self, value).value();
+ let result = visit_value::<_, Blocking>(self.as_visitor(), value).value();
assert_eq!(result, VisitResult::Control(Flow::Done));
}
+
+ fn visit_value_and_skipped<'a, T>(&'a mut self, value: T)
+ where
+ T: TypeName::LowerType<'a, 'ctx>,
+ 'ctx: 'a,
+ {
+ let result = visit_value::<_, Blocking>(self.as_visitor(), value).value();
+
+ assert_eq!(result.unit_skipped(), VisitResult::Skipped(()));
+ }
}
diff --git a/tests/serde_deserializer.rs b/tests/serde_deserializer.rs
index 0b4bd79..8b66291 100644
--- a/tests/serde_deserializer.rs
+++ b/tests/serde_deserializer.rs
@@ -1,6 +1,8 @@
use serde_json::json;
use treaty::walkers::serde::deserializer::DeserializerWalker;
-use treaty::BuildExt as _;
+use treaty::{Build, BuildExt as _};
+
+use macro_rules_attribute::derive;
mod common;
@@ -22,3 +24,18 @@ fn demo2() {
assert_eq!(y.unwrap(), 42);
}
+
+#[derive(Build!, Debug, PartialEq)]
+struct X {
+ a: bool,
+ b: i64,
+}
+
+#[test]
+fn demo3() {
+ let x = json!({ "a": true, "b": 42 });
+
+ let y = X::build(DeserializerWalker::new(x));
+
+ assert_eq!(y.unwrap(), X { a: true, b: 101 });
+}
diff --git a/tests/walker_struct.rs b/tests/walker_struct.rs
index 5898475..cf09be1 100644
--- a/tests/walker_struct.rs
+++ b/tests/walker_struct.rs
@@ -4,7 +4,7 @@ use treaty::{
effect::{blocking::Blocking, Effect, Effective, ErasedEffective},
protocol::{
visitor::{tags, SequenceProto, TagConst, TagProto, ValueProto, VisitResult},
- DynVisitor,
+ AsVisitor, DynVisitor,
},
walkers::core::r#struct::{StructTypeInfo, StructWalker},
Builder, DefaultMode, Flow, Walker,
@@ -133,9 +133,7 @@ fn sequence_of_field_values() {
});
assert_eq!(
- scope
- .next(Builder::<Blocking>::as_visitor(&mut visitor))
- .value(),
+ scope.next(AsVisitor::as_visitor(&mut visitor)).value(),
Flow::Continue
);
}
@@ -165,9 +163,7 @@ fn sequence_of_field_values() {
});
assert_eq!(
- scope
- .next(Builder::<Blocking>::as_visitor(&mut visitor))
- .value(),
+ scope.next(AsVisitor::as_visitor(&mut visitor)).value(),
Flow::Done
);
}
@@ -184,9 +180,7 @@ fn sequence_of_field_values() {
// Walk the struct.
assert_eq!(
- walker
- .walk(Builder::<Blocking>::as_visitor(&mut builder))
- .value(),
+ walker.walk(AsVisitor::as_visitor(&mut builder)).value(),
Ok(())
);
}
@@ -234,9 +228,7 @@ fn has_struct_tag() {
// Walk the struct.
assert_eq!(
- walker
- .walk(Builder::<Blocking>::as_visitor(&mut builder))
- .value(),
+ walker.walk(AsVisitor::as_visitor(&mut builder)).value(),
Ok(())
);
}
@@ -286,9 +278,7 @@ fn has_map_backup_tag() {
// Walk the struct.
assert_eq!(
- walker
- .walk(Builder::<Blocking>::as_visitor(&mut builder))
- .value(),
+ walker.walk(AsVisitor::as_visitor(&mut builder)).value(),
Ok(())
);
}
@@ -341,9 +331,7 @@ fn borrowed_value_directly() {
// Walk the struct.
assert_eq!(
- walker
- .walk(Builder::<Blocking>::as_visitor(&mut builder))
- .value(),
+ walker.walk(AsVisitor::as_visitor(&mut builder)).value(),
Ok(())
);
}