hit critical issue with serde's API
| -rw-r--r-- | src/build.rs | 9 | ||||
| -rw-r--r-- | src/build/builders/core/enum.rs | 11 | ||||
| -rw-r--r-- | src/build/builders/core/struct.rs | 12 | ||||
| -rw-r--r-- | src/build/builders/core/value.rs | 12 | ||||
| -rw-r--r-- | src/effect.rs | 52 | ||||
| -rw-r--r-- | src/macros/build.rs | 171 | ||||
| -rw-r--r-- | src/protocol.rs | 15 | ||||
| -rw-r--r-- | src/protocol/visitor/tag.rs | 4 | ||||
| -rw-r--r-- | src/walk/walkers/core/value.rs | 32 | ||||
| -rw-r--r-- | src/walk/walkers/serde/deserializer.rs | 91 | ||||
| -rw-r--r-- | tests/builder_enum.rs | 179 | ||||
| -rw-r--r-- | tests/builder_struct.rs | 13 | ||||
| -rw-r--r-- | tests/builder_value.rs | 8 | ||||
| -rw-r--r-- | tests/common/builder.rs | 11 | ||||
| -rw-r--r-- | tests/common/protocol/recoverable.rs | 31 | ||||
| -rw-r--r-- | tests/common/protocol/tag.rs | 25 | ||||
| -rw-r--r-- | tests/common/protocol/value.rs | 34 | ||||
| -rw-r--r-- | tests/serde_deserializer.rs | 19 | ||||
| -rw-r--r-- | tests/walker_struct.rs | 26 |
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(()) ); } |