Diffstat (limited to 'tests/builder_enum.rs')
| -rw-r--r-- | tests/builder_enum.rs | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/tests/builder_enum.rs b/tests/builder_enum.rs new file mode 100644 index 0000000..dbdc60f --- /dev/null +++ b/tests/builder_enum.rs @@ -0,0 +1,193 @@ +use treaty::{ + any::{OwnedStatic, TypeName}, + builders::core::r#enum::{EnumBuildInfo, EnumBuilder}, + effect::{ + blocking::Blocking, Effect, EffectExt as _, Effective as _, EffectiveExt as _, + ErasedEffective, + }, + protocol::{ + visitor::{tags, visit_recoverable, visit_tag, visit_value, DynRecoverableScope, TagConst}, + DynVisitor, + }, + walkers::core::value::ValueWalker, + Build, BuildExt as _, Builder, Flow, Status, WalkExt as _, +}; + +use crate::common::protocol::{ + recoverable::MockRecoverableScopeVisitor, value::ValueVisitorExt as _, +}; + +mod common; + +#[derive(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, + } + } + + fn marker_from_discriminant(discriminant: u32) -> Option<Self::VariantMarker> { + match discriminant { + 0 => Some(XMarker::A), + 1 => Some(XMarker::B), + _ => None, + } + } + + 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()))), + } + } + + 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), + }) + } +} + +#[test] +fn demo() { + 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)); + + assert_eq!(builder.build().value().unwrap(), X::A(1.23)); +} + +#[test] +fn demo2() { + let mut builder = X::new_builder(); + + // Use the recoverable flow. + { + 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); + + // The second builder will work. + scope.expect_new_walk().once().returning(|visitor| { + // The value for the B variant. + visitor.visit_value_and_done(OwnedStatic(true)); + + Status::Ok + }); + + // 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() + ); + } + + // The enum should have a value now. + assert_eq!(builder.build().value().unwrap(), X::B(true)); +} |