Diffstat (limited to 'tests/builder_enum.rs')
-rw-r--r--tests/builder_enum.rs193
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));
+}