use core::{marker::PhantomData, ops::ControlFlow};
use crate::{
any::{OwnedStatic, TempBorrowedStatic, TempBorrowedStaticHrt, TypeName},
any_trait,
effect::{Adapters, Effect, ObjSafe},
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 + core::fmt::Debug;
type Error: Send + Sync + core::fmt::Debug;
type T: Send + Sync;
fn new_builders<'a>(seed: Self::Seed) -> ObjSafe<'a, Self::Builders, E>;
fn from_builders<'a>(builders: Self::Builders) -> ObjSafe<'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>;
}
pub struct StructError<'ctx, I: StructTypeInfo<'ctx, M, E>, M, E: Effect> {
error: I::Error,
}
impl<'ctx, I: StructTypeInfo<'ctx, M, E>, M, E: Effect> core::fmt::Debug
for StructError<'ctx, I, M, E>
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("StructError")
.field("error", &self.error)
.finish()
}
}
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 = StructError<'ctx, I, M, E>;
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,
{
#[inline(always)]
fn from_seed<'a>(seed: Self::Seed) -> ObjSafe<'a, Self, E>
where
Self: 'a,
{
I::new_builders(seed)
.map(|builders| {
Self {
builders,
// Start in tuple mode until a struct or map tag is visited.
mode: StructMode::Tuple,
_generics: Default::default(),
}
})
.into()
}
fn build<'a>(self) -> ObjSafe<'a, Result<Self::Value, Self::Error>, E>
where
Self: 'a,
{
I::from_builders(self.builders)
.map(|builders| match builders {
Ok(value) => Ok(value),
Err(err) => Err(StructError { error: err }),
})
.into()
}
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,
{
#[inline(always)]
fn visit<'a>(
&'a mut self,
_kind: tags::Map,
walker: DynWalkerObjSafe<'a, 'ctx, E>,
) -> ObjSafe<'a, VisitResult<DynWalkerObjSafe<'a, 'ctx, E>>, E> {
// This signals to go into map mode for the sequence.
self.mode = StructMode::Map;
E::with(NoopVisitor::new(), |noop, _| {
(
walker
.walk(DynVisitor(noop))
.map(|x| x.to_continue().into())
.into(),
PhantomData,
)
})
.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,
{
#[inline(always)]
fn visit<'a>(
&'a mut self,
scope: DynSequenceScope<'a, 'ctx, E>,
) -> ObjSafe<'a, VisitResult<DynSequenceScope<'a, 'ctx, E>>, E> {
match self.mode {
StructMode::Tuple => {
E::ready((self, scope, 0))
.r#loop(|(this, scope, index)| {
if let Some(marker) = I::marker_from_index(*index) {
// Select the visitor for this field.
let visitor = I::as_visitor(marker, &mut this.builders);
// For rust analyzer.
let scope: &mut DynSequenceScope<'_, '_, E> = scope;
scope
.next(visitor)
.map(|flow| match flow {
Flow::Continue => {
*index += 1;
ControlFlow::Continue(())
}
Flow::Err => {
ControlFlow::Break(VisitResult::Control(Flow::Err))
}
Flow::Done => {
ControlFlow::Break(VisitResult::Control(Flow::Done))
}
})
.into()
} else {
E::ready(ControlFlow::Break(VisitResult::Control(Flow::Done))).into()
}
})
.into()
}
StructMode::Map => {
todo!()
}
}
// 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>,
{
#[inline(always)]
fn visit<'a>(
&'a mut self,
_key: tags::Key,
walker: DynWalkerObjSafe<'a, 'ctx, E>,
) -> ObjSafe<'a, VisitResult<DynWalkerObjSafe<'a, 'ctx, E>>, E> {
todo!()
// 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>,
ValueProto<OwnedStatic<&'static 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>,
{
#[inline(always)]
fn visit<'a>(
&'a mut self,
OwnedStatic(index): TypeName::T<'a, 'ctx, OwnedStatic<usize>>,
) -> ObjSafe<'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)).into()
}
}
impl<'ctx, I, M, E> Value<'ctx, TempBorrowedStaticHrt<str>, E> for NameVisitor<'ctx, I, M, E>
where
E: Effect,
I: StructTypeInfo<'ctx, M, E>,
{
#[inline(always)]
fn visit<'a>(
&'a mut self,
TempBorrowedStatic(name): TypeName::T<'a, 'ctx, TempBorrowedStaticHrt<str>>,
) -> ObjSafe<'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)).into()
}
}
impl<'ctx, I, M, E> Value<'ctx, OwnedStatic<&'static str>, E> for NameVisitor<'ctx, I, M, E>
where
E: Effect,
I: StructTypeInfo<'ctx, M, E>,
{
#[inline(always)]
fn visit<'a>(
&'a mut self,
OwnedStatic(name): TypeName::T<'a, 'ctx, OwnedStatic<&'static str>>,
) -> ObjSafe<'a, VisitResult<TypeName::T<'a, 'ctx, OwnedStatic<&'static 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)).into()
}
}