Diffstat (limited to 'src/build/builders/core/enum.rs')
-rw-r--r--src/build/builders/core/enum.rs277
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())
+ }
+ }
+}