Diffstat (limited to 'src/build/builders/core/struct.rs')
| -rw-r--r-- | src/build/builders/core/struct.rs | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/src/build/builders/core/struct.rs b/src/build/builders/core/struct.rs new file mode 100644 index 0000000..cff1ab3 --- /dev/null +++ b/src/build/builders/core/struct.rs @@ -0,0 +1,285 @@ +use crate::{ + any::{OwnedStatic, TempBorrowedStatic, TempBorrowedStaticHrt, TypeName}, + any_trait, + effect::{Effect, Future}, + hkt::Marker, + protocol::{ + visitor::{ + tags, DynSequenceScope, Sequence, SequenceProto, Tag, TagProto, Value, ValueProto, + VisitResult, + }, + DynVisitor, + }, + Builder, BuilderTypes, DynWalkerObjSafe, Flow, +}; + +use super::NoopVisitor; + +enum StructMode { + /// A tuple-like struct uses the order of the sequence. + Tuple, + + /// A map-like struct uses field names. + Map, +} + +pub struct StructBuilder<'ctx, I: StructTypeInfo<'ctx, M, E>, M, E: Effect> { + builders: I::Builders, + mode: StructMode, + _generics: Marker<E>, +} + +pub trait StructTypeInfo<'ctx, M, E: Effect>: 'static { + type Builders: Send + Sync; + + type Seed: Send + Sync; + + type FieldMarker: Send + Sync + Copy; + + type Error: Send + Sync; + + type T: Send + Sync; + + fn new_builders<'a>(seed: Self::Seed) -> Future<'a, Self::Builders, E>; + + fn from_builders<'a>(builders: Self::Builders) -> Future<'a, Result<Self::T, Self::Error>, E>; + + fn as_visitor<'a>( + marker: Self::FieldMarker, + builders: &'a mut Self::Builders, + ) -> DynVisitor<'a, 'ctx>; + + fn marker_from_index(index: usize) -> Option<Self::FieldMarker>; + fn marker_from_name(name: &str) -> Option<Self::FieldMarker>; +} + +impl<'ctx, I, M, E: Effect> BuilderTypes for StructBuilder<'ctx, I, M, E> +where + I: StructTypeInfo<'ctx, M, E>, +{ + type Seed = I::Seed; + + type Error = (); + + type Value = I::T; +} + +impl<'ctx, I, M, E> Builder<'ctx, E> for StructBuilder<'ctx, I, M, E> +where + I: StructTypeInfo<'ctx, M, E>, + E: Effect, +{ + fn from_seed<'a>(seed: Self::Seed) -> Future<'a, Self, E> + where + Self: 'a, + { + E::wrap(async { + Self { + builders: I::new_builders(seed).await, + // Start in tuple mode until a struct or map tag is visited. + mode: StructMode::Tuple, + _generics: Default::default(), + } + }) + } + + fn build<'a>(self) -> Future<'a, Result<Self::Value, Self::Error>, E> + where + Self: 'a, + { + E::wrap(async { + match I::from_builders(self.builders).await { + Ok(value) => Ok(value), + Err(_) => todo!(), + } + }) + } + + fn as_visitor(&mut self) -> DynVisitor<'_, 'ctx> { + DynVisitor(self) + } +} + +any_trait! { + impl['ctx, I, M, E] StructBuilder<'ctx, I, M, E> = [ + TagProto<tags::Map, E>, + SequenceProto<E> + ] where + E: Effect, + I: StructTypeInfo<'ctx, M, E>, +} + +impl<'ctx, I, M, E> Tag<'ctx, tags::Map, E> for StructBuilder<'ctx, I, M, E> +where + I: StructTypeInfo<'ctx, M, E>, + E: Effect, +{ + fn visit<'a>( + &'a mut self, + _kind: tags::Map, + walker: DynWalkerObjSafe<'a, 'ctx, E>, + ) -> Future<'a, VisitResult<DynWalkerObjSafe<'a, 'ctx, E>>, E> { + // This signals to go into map mode for the sequence. + self.mode = StructMode::Map; + + E::wrap(async { + walker + .walk(DynVisitor(&mut NoopVisitor::new())) + .await + .to_continue() + .into() + }) + } +} + +// A struct is a sequence of field values. +impl<'ctx, I, M, E> Sequence<'ctx, E> for StructBuilder<'ctx, I, M, E> +where + I: StructTypeInfo<'ctx, M, E>, + E: Effect, +{ + fn visit<'a>( + &'a mut self, + scope: DynSequenceScope<'a, 'ctx, E>, + ) -> Future<'a, VisitResult<DynSequenceScope<'a, 'ctx, E>>, E> { + match self.mode { + StructMode::Tuple => E::wrap(async { + let mut index = 0; + + // For each index based marker. + while let Some(marker) = I::marker_from_index(index) { + // Select the visitor for this field. + let visitor = I::as_visitor(marker, &mut self.builders); + + // Get the next value in the sequence. + match scope.next(visitor).await { + Flow::Continue => {} + Flow::Err => return VisitResult::Control(Flow::Err), + Flow::Done => break, + } + + // Move to the next field. + index += 1; + } + + VisitResult::Control(Flow::Done) + }), + StructMode::Map => E::wrap(async { + loop { + let mut visitor = FieldVisitor::<I, M, E> { + builders: &mut self.builders, + marker: None, + _marker: Default::default(), + }; + + match scope.next(DynVisitor(&mut visitor)).await { + Flow::Continue => {} + Flow::Err => return VisitResult::Control(Flow::Err), + Flow::Done => return VisitResult::Control(Flow::Done), + } + } + }), + } + } +} + +struct FieldVisitor<'a, 'ctx, I: StructTypeInfo<'ctx, M, E>, M, E: Effect> { + builders: &'a mut I::Builders, + marker: Option<I::FieldMarker>, + _marker: Marker<E>, +} + +any_trait! { + impl['ctx, 'a, I, M, E] FieldVisitor<'a, 'ctx, I, M, E> = [ + TagProto<tags::Key, E>, + ] else ref { + let (_this, _id); + None + } else mut { + let (this, id); + + this.marker.and_then(|marker| { + I::as_visitor(marker, this.builders).0.upcast_to_id_mut(id) + }) + } where + E: Effect, + I: StructTypeInfo<'ctx, M, E>, +} + +impl<'b, 'ctx, I, M, E> Tag<'ctx, tags::Key, E> for FieldVisitor<'b, 'ctx, I, M, E> +where + E: Effect, + I: StructTypeInfo<'ctx, M, E>, +{ + fn visit<'a>( + &'a mut self, + _key: tags::Key, + walker: DynWalkerObjSafe<'a, 'ctx, E>, + ) -> Future<'a, VisitResult<DynWalkerObjSafe<'a, 'ctx, E>>, E> { + E::wrap(async { + let mut visitor = NameVisitor::<I, M, E> { + field_marker: None, + _marker: Default::default(), + }; + + let flow = walker.walk(DynVisitor(&mut visitor)).await; + + self.marker = visitor.field_marker; + + // We are expecting the value of the field to be given next. + flow.to_continue().into() + }) + } +} + +struct NameVisitor<'ctx, I: StructTypeInfo<'ctx, M, E>, M, E: Effect> { + field_marker: Option<I::FieldMarker>, + _marker: Marker<E>, +} + +any_trait! { + impl['ctx, I, M, E] NameVisitor<'ctx, I, M, E> = [ + ValueProto<OwnedStatic<usize>, E>, + ValueProto<TempBorrowedStaticHrt<str>, E>, + ] where + E: Effect, + I: StructTypeInfo<'ctx, M, E>, +} + +impl<'ctx, I, M, E> Value<'ctx, OwnedStatic<usize>, E> for NameVisitor<'ctx, I, M, E> +where + E: Effect, + I: StructTypeInfo<'ctx, M, E>, +{ + fn visit<'a>( + &'a mut self, + OwnedStatic(index): TypeName::T<'a, 'ctx, OwnedStatic<usize>>, + ) -> Future<'a, VisitResult<TypeName::T<'a, 'ctx, OwnedStatic<usize>>>, E> + where + TypeName::T<'a, 'ctx, OwnedStatic<usize>>: Send + Sized, + 'ctx: 'a, + { + self.field_marker = I::marker_from_index(index); + + E::ready(VisitResult::Control(Flow::Done)) + } +} + +impl<'ctx, I, M, E> Value<'ctx, TempBorrowedStaticHrt<str>, E> for NameVisitor<'ctx, I, M, E> +where + E: Effect, + I: StructTypeInfo<'ctx, M, E>, +{ + fn visit<'a>( + &'a mut self, + TempBorrowedStatic(name): TypeName::T<'a, 'ctx, TempBorrowedStaticHrt<str>>, + ) -> Future<'a, VisitResult<TypeName::T<'a, 'ctx, TempBorrowedStaticHrt<str>>>, E> + where + TypeName::T<'a, 'ctx, TempBorrowedStaticHrt<str>>: Send + Sized, + 'ctx: 'a, + { + self.field_marker = I::marker_from_name(name); + + E::ready(VisitResult::Control(Flow::Done)) + } +} |