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))
}
}