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::{
any::{AnyTraitObject, TypeName, TypeNameId},
any_trait,
effect::{Effect, ErasedEffective, Ss},
hkt::Invariant,
protocol::{
visitor::{tags, Tag, TagProto, Value, ValueProto, VisitResult},
DynVisitor,
},
Builder, BuilderTypes, DynWalkerObjSafe, Flow,
};
pub struct EnumBuilder<'ctx, Info, Mode, E: Effect>
where
Info: EnumBuildInfo<'ctx, Mode, E>,
{
inner: Inner<'ctx, Info, Mode, E>,
}
enum Inner<'ctx, Info, Mode, E: Effect>
where
Info: EnumBuildInfo<'ctx, Mode, E>,
{
Temp,
Seed(Info::Seed),
Builder { builder: Info::Builders },
Value(Result<Info::T, Info::Error>),
}
pub trait EnumBuildInfo<'ctx, Mode, E: Effect> {
type Builders: Ss;
type Seed: Ss;
type Error: Ss + Debug + Display;
type ValueT: TypeName::MemberType;
type T: Ss;
type VariantMarker: Ss + Copy + Display;
fn new_builder<'a>(
seed: Self::Seed,
variant: Self::VariantMarker,
) -> ErasedEffective<'a, Self::Builders, E>;
fn finish_builder<'a>(
builder: Self::Builders,
) -> ErasedEffective<'a, Result<Self::T, Self::Error>, E>;
fn from_value<'a>(value: TypeName::T<'a, 'ctx, Self::ValueT>) -> Self::T;
fn as_visitor<'a>(builder: &'a mut Self::Builders) -> DynVisitor<'a, 'ctx>;
fn marker_from_name(name: &str) -> Option<Self::VariantMarker>;
fn marker_from_discriminant(discriminant: u32) -> Option<Self::VariantMarker>;
fn guess_variant<'a>(
seed: Self::Seed,
scope: DynRecoverableScope<'a, 'ctx, E>,
) -> ErasedEffective<'a, Result<Self::T, Self::Error>, E>;
}
impl<'ctx, Info, Mode, E: Effect> BuilderTypes for EnumBuilder<'ctx, Info, Mode, E>
where
Info: EnumBuildInfo<'ctx, Mode, E>,
{
type Seed = Info::Seed;
type Error = Info::Error;
type Value = Info::T;
}
impl<'ctx, Info, Mode, E: Effect> Builder<'ctx, E> for EnumBuilder<'ctx, Info, Mode, E>
where
Info: EnumBuildInfo<'ctx, Mode, E>,
{
fn from_seed<'a>(seed: Self::Seed) -> ErasedEffective<'a, Self, E>
where
Self: 'a,
{
E::ready(Self {
inner: Inner::Seed(seed),
})
}
fn build<'a>(self) -> ErasedEffective<'a, Result<Self::Value, Self::Error>, E>
where
Self: 'a,
{
match self.inner {
Inner::Temp => unreachable!(),
Inner::Seed(seed) => {
// what to do...
todo!()
}
Inner::Builder { builder } => Info::finish_builder(builder),
Inner::Value(value) => E::ready(value),
}
}
fn as_visitor(&mut self) -> DynVisitor<'_, 'ctx> {
DynVisitor(self)
}
}
any_trait! {
impl['ctx, Info, Mode, E] EnumBuilder<'ctx, Info, Mode, E> = [
ValueProto<Info::ValueT, E>,
TagProto<tags::Variant, E>,
RecoverableProto<E>
] ref {
let (_this, _id);
} else ref {
None
} mut {
let (this, id);
// If a variant has been chosen, then forward everything to it's builder.
if matches!(this.inner, Inner::Builder { .. }) {
match &mut this.inner {
Inner::Builder { builder } => {
return Info::as_visitor(builder).0.upcast_to_id_mut(id)
}
_ => unreachable!(),
}
}
} else mut {
None
} where
E: Effect,
Info: EnumBuildInfo<'ctx, Mode, E>,
}
impl<'ctx, Info, Mode, E: Effect> Recoverable<'ctx, E> for EnumBuilder<'ctx, Info, Mode, E>
where
Info: EnumBuildInfo<'ctx, Mode, E>,
{
fn visit<'a>(
&'a mut self,
scope: DynRecoverableScope<'a, 'ctx, E>,
) -> ErasedEffective<'a, VisitResult, E> {
match core::mem::replace(&mut self.inner, Inner::Temp) {
Inner::Seed(seed) => Info::guess_variant(seed, scope).map(|result| {
self.inner = Inner::Value(result);
Flow::Done.into()
}),
inner => {
self.inner = inner;
E::ready(Flow::Continue.into())
}
}
}
}
impl<'ctx, Info, Mode, E: Effect> Value<'ctx, Info::ValueT, E> for EnumBuilder<'ctx, Info, Mode, E>
where
Info: EnumBuildInfo<'ctx, Mode, E>,
{
fn visit<'a>(
&'a mut self,
value: TypeName::T<'a, 'ctx, Info::ValueT>,
) -> ErasedEffective<'a, VisitResult<TypeName::T<'a, 'ctx, Info::ValueT>>, E>
where
TypeName::T<'a, 'ctx, Info::ValueT>: Send + Sync + Sized,
'ctx: 'a,
{
self.inner = Inner::Value(Ok(Info::from_value(value)));
E::ready(Flow::Done.into())
}
}
impl<'ctx, Info, Mode, E: Effect> Tag<'ctx, tags::Variant, E> for EnumBuilder<'ctx, Info, Mode, E>
where
Info: EnumBuildInfo<'ctx, Mode, E>,
{
fn visit<'a: 'c, 'b: 'c, 'c>(
&'a mut self,
_kind: tags::Variant,
walker: DynWalkerObjSafe<'b, 'ctx, E>,
) -> ErasedEffective<'c, VisitResult, E> {
let visitor = VariantVisitor::<Info, Mode, E> { marker: None };
E::as_ctx((visitor, walker), |(visitor, walker)| {
walker.walk(DynVisitor(visitor)).cast()
})
.then(|((visitor, _), result)| {
if let Some(variant) = visitor.marker {
match core::mem::replace(&mut self.inner, Inner::Temp) {
// A variant was given so we need to make the builder for
// it.
Inner::Seed(seed) => Info::new_builder(seed, variant).map(move |builder| {
self.inner = Inner::Builder { builder };
result.to_done().into()
}),
inner => {
self.inner = inner;
E::ready(result.to_done().into())
}
}
} else {
E::ready(result.to_done().into())
}
})
}
}
struct VariantVisitor<'ctx, Info, Mode, E: Effect>
where
Info: EnumBuildInfo<'ctx, Mode, E>,
{
marker: Option<Info::VariantMarker>,
}
any_trait! {
impl['ctx, Info, Mode, E] VariantVisitor<'ctx, Info, Mode, E> = [
ValueProto<TempBorrowedStaticHrt<str>, E>,
ValueProto<OwnedStatic<u32>, E>,
]
where
E: Effect,
Info: EnumBuildInfo<'ctx, Mode, E>,
}
impl<'ctx, Info, Mode, E: Effect> Value<'ctx, TempBorrowedStaticHrt<str>, E>
for VariantVisitor<'ctx, Info, Mode, E>
where
Info: EnumBuildInfo<'ctx, Mode, E>,
{
fn visit<'a>(
&'a mut self,
TempBorrowedStatic(value): TypeName::T<'a, 'ctx, TempBorrowedStaticHrt<str>>,
) -> ErasedEffective<'a, VisitResult<TypeName::T<'a, 'ctx, TempBorrowedStaticHrt<str>>>, E>
where
TypeName::T<'a, 'ctx, TempBorrowedStaticHrt<str>>: Send + Sync + Sized,
'ctx: 'a,
{
if let Some(variant) = Info::marker_from_name(value) {
self.marker = Some(variant);
E::ready(Flow::Done.into())
} else {
E::ready(Flow::Continue.into())
}
}
}
impl<'ctx, Info, Mode, E: Effect> Value<'ctx, OwnedStatic<u32>, E>
for VariantVisitor<'ctx, Info, Mode, E>
where
Info: EnumBuildInfo<'ctx, Mode, E>,
{
fn visit<'a>(
&'a mut self,
OwnedStatic(value): TypeName::T<'a, 'ctx, OwnedStatic<u32>>,
) -> ErasedEffective<'a, VisitResult<TypeName::T<'a, 'ctx, OwnedStatic<u32>>>, E>
where
TypeName::T<'a, 'ctx, OwnedStatic<u32>>: Send + Sync + Sized,
'ctx: 'a,
{
if let Some(variant) = Info::marker_from_discriminant(value) {
self.marker = Some(variant);
E::ready(Flow::Done.into())
} else {
E::ready(Flow::Continue.into())
}
}
}