use core::any::TypeId;
use crate::{
any::{AnyTrait, BorrowedStatic, BorrowedStaticHrt, StaticType},
any_trait,
effect::{Effect, EffectExt as _, Effective, EffectiveExt, ErasedEffective, ReadyExt as _},
hkt::Marker,
never::Never,
protocol::{
visitor::{
request_hint, tags, visit_sequence, visit_tag, visit_value, EffectiveVisitExt as _,
Recoverable, RecoverableKnown, RecoverableProto, RecoverableScope, Sequence,
SequenceKnown, SequenceProto, SequenceScope, Tag, TagConst, TagDyn, TagError, TagKnown,
TagProto, Value, ValueKnown, ValueProto, VisitResult,
},
walker::hint::{Hint, HintMeta, HintProto, MetaHint, MetaKnown},
DynVisitor, DynWalker,
},
DynWalkerAdapter, Flow, Status, TAG_FIELD_NAMES, TAG_MAP, TAG_STRUCT, TAG_TYPE_ID,
TAG_TYPE_NAME,
};
use super::{noop::NoopWalker, value::ValueWalker};
/// Walker for a borrow of a struct.
///
/// This walker implements the struct flow. The struct cannot contain lifetimes.
pub struct StructWalker<'ctx, I: StructTypeInfo<'ctx, M, E, S = S>, S, M, E: Effect> {
/// Struct value to walk.
value: &'ctx I::T,
/// Index of the current field to walk.
index: usize,
/// Error if there was one.
///
/// The visitor tracks it's own errors.
error: Option<StructWalkErrorKind<I::FieldError>>,
_generics: Marker<E>,
}
/// Type info about a struct needed by [`StructWalker`].
pub trait StructTypeInfo<'ctx, M, E: Effect>: 'static {
/// Name of the struct.
const NAME: &'static str;
/// The field names in definition order.
const FIELDS: &'static [&'static str];
/// The walking errors for the fields.
type FieldError: Send + Sync + core::fmt::Debug;
type S: 'static;
/// The struct being described.
type T: Send + Sync;
/// Walk the given field.
fn walk_field<'a>(
index: usize,
value: &'ctx Self::T,
visitor: DynVisitor<'a, 'ctx>,
) -> ErasedEffective<'a, Result<Flow, Self::FieldError>, E>;
}
#[derive(Debug, PartialEq, Clone, Copy)]
#[allow(unused)]
enum StructWalkErrorKind<T> {
/// Error with visiting a tag for the struct.
///
/// This error shouldn't really happen if the visitor behaves.
Tag(TagError<Never>),
/// Error with visiting a tag for a struct's field.
///
/// This error shouldn't really happen if the visitor behaves.
FieldTag(TagError<TagError<Never>>),
/// Error with visiting a field.
Field(T),
}
/// Error from walking a struct.
#[derive(Debug, PartialEq, Copy, Clone)]
#[allow(unused)]
pub struct StructWalkError<T> {
kind: StructWalkErrorKind<T>,
}
impl<'ctx, I, S, M, E: Effect> StructWalker<'ctx, I, S, M, E>
where
I: StructTypeInfo<'ctx, M, E, S = S>,
{
/// Create walker from a borrow of a struct.
pub fn new(value: &'ctx I::T) -> Self {
Self {
value,
index: 0,
error: None,
_generics: Default::default(),
}
}
fn record_tag_error<U>(
&mut self,
result: Result<VisitResult<U>, TagError<Never>>,
) -> VisitResult<U> {
result.unwrap_or_else(|err| {
self.error = Some(StructWalkErrorKind::Tag(err));
Flow::Err.into()
})
}
}
impl<'ctx, I, S, E, M> crate::Walker<'ctx, E> for StructWalker<'ctx, I, S, M, E>
where
E: Effect,
I: StructTypeInfo<'ctx, M, E, S = S>,
Self: AnyTrait<'ctx> + RecoverableScope<'ctx, E>,
{
type Error = StructWalkError<I::FieldError>;
type Output = ();
#[inline(always)]
fn walk<'b: 'c, 'c>(
self,
visitor: DynVisitor<'b, 'ctx>,
) -> ErasedEffective<'c, Result<Self::Output, Self::Error>, E>
where
Self: 'c,
{
E::ready((self, visitor))
.as_ctx(
#[inline(always)]
|(this, visitor)| {
RecoverableScope::<'ctx, E>::new_walk::<'_, '_, '_>(this, visitor.cast()).cast()
},
)
.map(|((this, _), _)| match this.error {
Some(err) => Err(StructWalkError { kind: err }),
None => Ok(()),
})
// E::ready(self).as_ctx_for::<'ctx, '_>(|this, _| {
// (RecoverableScope::<'ctx, E>::new_walk::<'_, 'b, '_>(this, visitor), PhantomData)
// });
// E::wrap(async move {
// // Use the recoverable walk to not duplicate the code.
// let _ = RecoverableScope::<'ctx, E>::new_walk(&mut self, visitor.cast()).await;
//
// // Get the error if there was one.
// match self.error {
// Some(err) => Err(StructWalkError { kind: err }),
// None => Ok(()),
// }
// })
}
}
any_trait! {
impl['ctx, I, M, E] StructWalker<'ctx, I, StaticType, M, E> = [
HintProto<RecoverableProto<E>>,
HintProto<SequenceProto<E>>,
HintProto<ValueProto<BorrowedStaticHrt<I::T>, E>>,
HintProto<TagProto<TagDyn, E>>,
HintProto<TagProto<TagConst<{ TAG_TYPE_ID.to_int() }>, E>>,
HintProto<TagProto<TagConst<{ TAG_STRUCT.to_int() }>, E>>,
HintProto<TagProto<TagConst<{ TAG_MAP.to_int() }>, E>>,
HintProto<TagProto<TagConst<{ TAG_TYPE_NAME.to_int() }>, E>>,
HintProto<TagProto<TagConst<{ TAG_FIELD_NAMES.to_int() }>, E>>,
] where
E: Effect,
I: StructTypeInfo<'ctx, M, E, S = StaticType>,
M: 'ctx,
I::T: 'static
}
impl<'ctx, I, S, M, E> Hint<'ctx, RecoverableProto<E>> for StructWalker<'ctx, I, S, M, E>
where
E: Effect,
I: StructTypeInfo<'ctx, M, E, S = S>,
Self: AnyTrait<'ctx> + RecoverableScope<'ctx, E>,
{
#[inline(always)]
fn hint<'this, 'visitor, 'hint, 'e>(
&'this mut self,
_visitor: &'visitor mut (dyn Recoverable<'ctx, E> + Send + Sync),
_hint: MetaHint<'hint, 'ctx, RecoverableProto<E>>,
) -> ErasedEffective<'e, Flow, E>
where
'ctx: 'this + 'visitor + 'hint + 'e,
{
todo!()
// E::map(
// visit_recoverable::<E>(visitor, self),
// |status| match status {
// VisitResult::Skipped(_) => Flow::Continue,
// VisitResult::Control(flow) => flow,
// },
// )
}
#[inline(always)]
fn known<'a>(
&'a mut self,
_hint: &'a <RecoverableProto<E> as HintMeta>::Hint,
) -> ErasedEffective<'a, Result<MetaKnown<'a, 'ctx, RecoverableProto<E>>, ()>, E> {
E::ready(Ok(RecoverableKnown))
}
}
impl<'ctx, I, S, M, E> Hint<'ctx, TagProto<TagConst<{ TAG_FIELD_NAMES.to_int() }>, E>>
for StructWalker<'ctx, I, S, M, E>
where
E: Effect,
I: StructTypeInfo<'ctx, M, E, S = S>,
{
#[inline(always)]
fn hint<'this, 'visitor, 'hint, 'e>(
&'this mut self,
_visitor: &'visitor mut (dyn Tag<'ctx, tags::FieldNames, E> + Send + Sync),
_hint: MetaHint<'hint, 'ctx, TagProto<TagConst<{ TAG_FIELD_NAMES.to_int() }>, E>>,
) -> ErasedEffective<'e, Flow, E>
where
'ctx: 'this + 'visitor + 'hint + 'e,
{
todo!()
// E::map(
// visit_tag::<TagConst<{ TAG_FIELD_NAMES.to_int() }>, E, _>(
// TagConst,
// visitor,
// StaticSliceWalker::<_, ValueWalker<&'static str>>::new(I::FIELDS),
// ),
// |status| match status {
// Err(err) => {
// self.error = Some(StructWalkErrorKind::FieldTag(err));
// Flow::Err
// }
// Ok(VisitResult::Skipped(_)) => Flow::Continue,
// Ok(VisitResult::Control(flow)) => flow,
// },
// )
}
#[inline(always)]
fn known<'a>(
&'a mut self,
_hint: &'a MetaHint<'a, 'ctx, TagProto<TagConst<{ TAG_FIELD_NAMES.to_int() }>, E>>,
) -> ErasedEffective<
'a,
Result<MetaKnown<'a, 'ctx, TagProto<TagConst<{ TAG_FIELD_NAMES.to_int() }>, E>>, ()>,
E,
> {
E::ready(Ok(TagKnown {
kind_available: Some(true),
}))
}
}
impl<'ctx, I, S, M, E> Hint<'ctx, TagProto<TagConst<{ TAG_TYPE_NAME.to_int() }>, E>>
for StructWalker<'ctx, I, S, M, E>
where
E: Effect,
I: StructTypeInfo<'ctx, M, E, S = S>,
{
#[inline(always)]
fn hint<'this, 'visitor, 'hint, 'e>(
&'this mut self,
_visitor: &'visitor mut (dyn Tag<'ctx, tags::TypeName, E> + Send + Sync),
_hint: MetaHint<'hint, 'ctx, TagProto<TagConst<{ TAG_TYPE_NAME.to_int() }>, E>>,
) -> ErasedEffective<'e, Flow, E>
where
'ctx: 'this + 'visitor + 'hint + 'e,
{
todo!()
// E::map(
// visit_tag::<TagConst<{ TAG_TYPE_NAME.to_int() }>, E, _>(
// TagConst,
// visitor,
// ValueWalker::new(I::NAME),
// ),
// |status| match status {
// Err(err) => {
// self.error = Some(StructWalkErrorKind::Tag(err));
// Flow::Err
// }
// Ok(VisitResult::Skipped(_)) => Flow::Continue,
// Ok(VisitResult::Control(flow)) => flow,
// },
// )
}
#[inline(always)]
fn known<'a>(
&'a mut self,
_hint: &'a <TagProto<TagConst<{ TAG_TYPE_NAME.to_int() }>, E> as HintMeta>::Hint,
) -> ErasedEffective<
'a,
Result<MetaKnown<'a, 'ctx, TagProto<TagConst<{ TAG_TYPE_NAME.to_int() }>, E>>, ()>,
E,
> {
E::ready(Ok(TagKnown {
kind_available: Some(true),
}))
}
}
impl<'ctx, I, S, M, E> Hint<'ctx, TagProto<TagConst<{ TAG_MAP.to_int() }>, E>>
for StructWalker<'ctx, I, S, M, E>
where
E: Effect,
I: StructTypeInfo<'ctx, M, E, S = S>,
{
#[inline(always)]
fn hint<'this, 'visitor, 'hint, 'e>(
&'this mut self,
_visitor: &'visitor mut (dyn Tag<'ctx, tags::Map, E> + Send + Sync),
_hint: MetaHint<'hint, 'ctx, TagProto<TagConst<{ TAG_MAP.to_int() }>, E>>,
) -> ErasedEffective<'e, Flow, E>
where
'ctx: 'this + 'visitor + 'hint + 'e,
{
todo!()
// E::map(
// visit_tag::<TagConst<{ TAG_MAP.to_int() }>, E, _>(TagConst, visitor, NoopWalker::new()),
// |status| match status {
// Err(err) => {
// self.error = Some(StructWalkErrorKind::Tag(err));
// Flow::Err
// }
// Ok(VisitResult::Skipped(_)) => Flow::Continue,
// Ok(VisitResult::Control(flow)) => flow,
// },
// )
}
#[inline(always)]
fn known<'a>(
&'a mut self,
_hint: &'a <TagProto<TagConst<{ TAG_MAP.to_int() }>, E> as HintMeta>::Hint,
) -> ErasedEffective<
'a,
Result<MetaKnown<'a, 'ctx, TagProto<TagConst<{ TAG_MAP.to_int() }>, E>>, ()>,
E,
> {
E::ready(Ok(TagKnown {
kind_available: Some(true),
}))
}
}
impl<'ctx, I, S, M, E> Hint<'ctx, TagProto<TagConst<{ TAG_STRUCT.to_int() }>, E>>
for StructWalker<'ctx, I, S, M, E>
where
E: Effect,
I: StructTypeInfo<'ctx, M, E, S = S>,
{
#[inline(always)]
fn hint<'this: 'e, 'visitor: 'e, 'hint: 'e, 'e>(
&'this mut self,
visitor: &'visitor mut (dyn Tag<'ctx, tags::Struct, E> + Send + Sync),
_hint: MetaHint<'hint, 'ctx, TagProto<TagConst<{ TAG_STRUCT.to_int() }>, E>>,
) -> ErasedEffective<'e, Flow, E>
where
'ctx: 'this + 'visitor + 'hint + 'e,
{
E::as_ctx(
(visitor, DynWalkerAdapter::new(NoopWalker::new())),
#[inline(always)]
|(visitor, noop_walker)| {
visitor
.visit(TagConst, noop_walker)
.map(|status| status.to_flow().unwrap_or(Flow::Continue))
.cast()
},
)
.remove_ctx()
}
#[inline(always)]
fn known<'a>(
&'a mut self,
_hint: &'a <TagProto<TagConst<{ TAG_STRUCT.to_int() }>, E> as HintMeta>::Hint,
) -> ErasedEffective<
'a,
Result<MetaKnown<'a, 'ctx, TagProto<TagConst<{ TAG_STRUCT.to_int() }>, E>>, ()>,
E,
> {
E::ready(Ok(TagKnown {
kind_available: Some(true),
}))
}
}
impl<'ctx, I, M, E> Hint<'ctx, TagProto<TagConst<{ TAG_TYPE_ID.to_int() }>, E>>
for StructWalker<'ctx, I, StaticType, M, E>
where
E: Effect,
I: StructTypeInfo<'ctx, M, E, S = StaticType>,
I::T: 'static,
{
#[inline(always)]
fn hint<'this, 'visitor, 'hint, 'e>(
&'this mut self,
_visitor: &'visitor mut (dyn Tag<'ctx, tags::TypeId, E> + Send + Sync),
_hint: MetaHint<'hint, 'ctx, TagProto<TagConst<{ TAG_TYPE_ID.to_int() }>, E>>,
) -> ErasedEffective<'e, Flow, E>
where
'ctx: 'this + 'visitor + 'hint + 'e,
{
todo!()
// E::map(
// visit_tag::<TagConst<{ TAG_TYPE_ID.to_int() }>, E, _>(
// TagConst,
// visitor,
// ValueWalker::new(TypeId::of::<I::T>()),
// ),
// |status| match status {
// Err(err) => {
// self.error = Some(StructWalkErrorKind::Tag(err));
// Flow::Err
// }
// Ok(VisitResult::Skipped(_)) => Flow::Continue,
// Ok(VisitResult::Control(flow)) => flow,
// },
// )
}
#[inline(always)]
fn known<'a>(
&'a mut self,
_hint: &'a <TagProto<TagConst<{ TAG_TYPE_ID.to_int() }>, E> as HintMeta>::Hint,
) -> ErasedEffective<
'a,
Result<MetaKnown<'a, 'ctx, TagProto<TagConst<{ TAG_TYPE_ID.to_int() }>, E>>, ()>,
E,
> {
E::ready(Ok(TagKnown {
kind_available: Some(true),
}))
}
}
impl<'ctx, I, M, E> Hint<'ctx, TagProto<TagDyn, E>> for StructWalker<'ctx, I, StaticType, M, E>
where
E: Effect,
I: StructTypeInfo<'ctx, M, E, S = StaticType>,
I::T: 'static,
{
#[inline(always)]
fn hint<'this: 'e, 'visitor: 'e, 'hint: 'e, 'e>(
&'this mut self,
_visitor: &'visitor mut (dyn Tag<'ctx, TagDyn, E> + Send + Sync),
_hint: MetaHint<'hint, 'ctx, TagProto<TagDyn, E>>,
) -> ErasedEffective<'e, Flow, E>
where
'ctx: 'this + 'visitor + 'hint + 'e,
{
todo!()
// match hint.kind.0 {
// crate::TAG_TYPE_ID => {
// Hint::<'ctx, TagProto<TagConst<{ TAG_TYPE_ID.to_int() }>, E>>::hint(
// self,
// visitor,
// TagHint { kind: TagConst },
// )
// }
// crate::TAG_STRUCT => {
// Hint::<'ctx, TagProto<TagConst<{ TAG_STRUCT.to_int() }>, E>>::hint(
// self,
// visitor,
// TagHint { kind: TagConst },
// )
// }
// crate::TAG_MAP => Hint::<'ctx, TagProto<TagConst<{ TAG_MAP.to_int() }>, E>>::hint(
// self,
// visitor,
// TagHint { kind: TagConst },
// ),
// crate::TAG_TYPE_NAME => {
// Hint::<'ctx, TagProto<TagConst<{ TAG_TYPE_NAME.to_int() }>, E>>::hint(
// self,
// visitor,
// TagHint { kind: TagConst },
// )
// }
// crate::TAG_FIELD_NAMES => Hint::<
// 'ctx,
// TagProto<TagConst<{ TAG_FIELD_NAMES.to_int() }>, E>,
// >::hint(self, visitor, TagHint { kind: TagConst }),
// _ => E::ready(Flow::Continue),
// }
}
#[inline(always)]
fn known<'a>(
&'a mut self,
hint: &'a <TagProto<TagDyn, E> as HintMeta>::Hint,
) -> ErasedEffective<'a, Result<MetaKnown<'a, 'ctx, TagProto<TagDyn, E>>, ()>, E> {
E::ready(match hint.kind {
TagDyn(crate::TAG_TYPE_ID) | TagDyn(crate::TAG_STRUCT) => Ok(TagKnown {
kind_available: Some(true),
}),
_ => Ok(TagKnown {
kind_available: Some(false),
}),
})
}
}
impl<'ctx, I, M, E> Hint<'ctx, ValueProto<BorrowedStaticHrt<I::T>, E>>
for StructWalker<'ctx, I, StaticType, M, E>
where
E: Effect,
I: StructTypeInfo<'ctx, M, E, S = StaticType>,
I::T: 'static,
{
#[inline(always)]
fn hint<'this, 'visitor, 'hint, 'e>(
&'this mut self,
_visitor: &'visitor mut (dyn Value<'ctx, BorrowedStaticHrt<I::T>, E> + Send + Sync),
_hint: MetaHint<'hint, 'ctx, ValueProto<BorrowedStaticHrt<I::T>, E>>,
) -> ErasedEffective<'e, Flow, E>
where
'ctx: 'this + 'visitor + 'hint + 'e,
{
todo!()
// E::map(
// visit_value::<_, E>(visitor, BorrowedStatic(self.value)),
// |status| match status {
// VisitResult::Skipped(_) => Flow::Continue,
// VisitResult::Control(flow) => flow,
// },
// )
}
#[inline(always)]
fn known<'a>(
&'a mut self,
_hint: &'a (),
) -> ErasedEffective<'a, Result<ValueKnown<'a, BorrowedStatic<'ctx, I::T>>, ()>, E> {
E::ready(Ok(ValueKnown { preview: None }))
}
}
impl<'ctx, I, S, M: 'ctx, E> Hint<'ctx, SequenceProto<E>> for StructWalker<'ctx, I, S, M, E>
where
E: Effect,
I: StructTypeInfo<'ctx, M, E, S = S>,
{
#[inline(always)]
fn hint<'this: 'e, 'visitor: 'e, 'hint: 'e, 'e>(
&'this mut self,
visitor: &'visitor mut (dyn Sequence<'ctx, E> + Send + Sync),
_hint: MetaHint<'hint, 'ctx, SequenceProto<E>>,
) -> ErasedEffective<'e, Flow, E>
where
'ctx: 'this + 'visitor + 'hint + 'e,
{
E::as_ctx_map((self, visitor), |(this, visitor)| {
visitor
.visit(*this)
.map(|status| match status {
VisitResult::Skipped(_) => Flow::Continue,
VisitResult::Control(flow) => flow,
})
.cast()
})
}
#[inline(always)]
fn known<'a>(
&'a mut self,
_hint: &'a <SequenceProto<E> as HintMeta>::Hint,
) -> ErasedEffective<'a, Result<MetaKnown<'a, 'ctx, SequenceProto<E>>, ()>, E> {
let len = I::FIELDS.len();
E::ready(Ok(SequenceKnown {
len: (len, Some(len)),
}))
}
}
impl<'ctx, I, S, M: 'ctx, E> SequenceScope<'ctx, E> for StructWalker<'ctx, I, S, M, E>
where
E: Effect,
I: StructTypeInfo<'ctx, M, E, S = S>,
{
#[inline(always)]
fn size_hint(&mut self) -> ErasedEffective<'_, (usize, Option<usize>), E> {
let len = I::FIELDS.len();
E::ready((len, Some(len)))
}
#[inline(always)]
fn next<'a: 'c, 'b: 'c, 'c>(
&'a mut self,
visitor: DynVisitor<'b, 'ctx>,
) -> ErasedEffective<'c, Flow, E> {
if self.index >= I::FIELDS.len() {
return Flow::Done.ready();
}
let index = self.index;
self.index += 1;
I::walk_field(index, self.value, visitor).map(|result| match result {
Ok(flow) => flow,
Err(err) => {
// Record the error and signal a break.
self.error = Some(StructWalkErrorKind::Field(err));
Flow::Err
}
})
// E::map(
// I::walk_field::<E>(index, self.value, visitor),
// |result| match result {
// Ok(flow) => flow,
// Err(err) => {
// // Record the error and signal a break.
// self.error = Some(StructWalkErrorKind::Field(err));
// Flow::Err
// }
// },
// )
}
}
impl<'ctx, I, M: 'ctx, E> RecoverableScope<'ctx, E> for StructWalker<'ctx, I, StaticType, M, E>
where
E: Effect,
I: StructTypeInfo<'ctx, M, E, S = StaticType>,
I::T: 'static,
{
#[inline(always)]
fn new_walk<'a: 'c, 'b: 'c, 'c>(
&'a mut self,
visitor: DynVisitor<'b, 'ctx>,
) -> ErasedEffective<'c, Status, E> {
// Reset the errors to default state.
self.error = None;
// Reset the field index to the default.
self.index = 0;
E::as_ctx((self, visitor), |(this, visitor)| {
request_hint::<E>(visitor.cast(), DynWalker(*this))
.map(VisitResult::unit_skipped)
.cast()
})
.if_not_finished(|(this, visitor)| {
visit_value::<_, E>(visitor.cast(), BorrowedStatic(this.value))
.map(VisitResult::unit_skipped)
.cast()
})
.if_not_finished(|(this, visitor)| {
visit_tag::<TagConst<{ TAG_TYPE_ID.to_int() }>, E, _>(
TagConst,
visitor.cast(),
ValueWalker::new(TypeId::of::<I::T>()),
)
.map(|result| this.record_tag_error(result).unit_skipped())
.cast()
})
.if_not_finished(|(this, visitor)| {
visit_tag::<TagConst<{ TAG_STRUCT.to_int() }>, E, _>(
TagConst,
visitor.cast(),
NoopWalker::new(),
)
.map(|result| this.record_tag_error(result).unit_skipped())
.cast()
})
.if_skipped(|(this, visitor)| {
visit_tag::<TagConst<{ TAG_MAP.to_int() }>, E, _>(
TagConst,
visitor.cast(),
NoopWalker::new(),
)
.map(|result| this.record_tag_error(result).unit_skipped())
.cast()
})
.if_not_finished(|(this, visitor)| {
visit_sequence::<E>(visitor.cast(), *this)
.map(VisitResult::unit_skipped)
.cast()
})
.remove_ctx()
.map(VisitResult::to_status)
}
}