Diffstat (limited to 'src/build/builders/core/enum.rs')
| -rw-r--r-- | src/build/builders/core/enum.rs | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/src/build/builders/core/enum.rs b/src/build/builders/core/enum.rs new file mode 100644 index 0000000..92e3149 --- /dev/null +++ b/src/build/builders/core/enum.rs @@ -0,0 +1,277 @@ +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<DynRecoverableScope<'a, 'ctx, E>>, 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<DynWalkerObjSafe<'b, 'ctx, E>>, 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()) + } + } +} |