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