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));
}