use core::any::TypeId;
use crate::{
any::static_wrapper::{BorrowedStatic, DynBorrowedStatic}, any_trait, effect::{Effect, Future}, hkt::Marker, never::Never, protocol::{
visitor::{
recoverable::{visit_recoverable, DynRecoverable, RecoverableScope},
request_hint::visit_request_hint,
sequence::{visit_sequence, DynSequence, SequenceKnown, SequenceScope},
tag::{visit_tag, DynTag, TagConst, TagDyn, TagError, TagHint, TagKnown},
value::{visit_value, DynValue, ValueKnown},
Status,
},
walker::hint::{DynHint, HintMeta},
walker::hint::{Hint, Known},
Visitor,
}, Flow, WalkerTypes, TAG_FIELD_NAMES, TAG_MAP, TAG_STRUCT, TAG_TYPE_ID, TAG_TYPE_NAME
};
use super::{noop::NoopWalker, tag::StaticSliceWalker, value::ValueWalker};
/// Walker for a borrow of a struct.
///
/// This walker implements the struct flow. The struct cannot contain lifetimes.
pub struct StructWalker<'ctx, T, I: StructTypeInfo<'ctx, M>, M, E> {
/// Struct value to walk.
value: &'ctx 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<(I, M, E)>,
}
/// Type info about a struct needed by [`StructWalker`].
pub trait StructTypeInfo<'ctx, M>: '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;
/// The struct being described.
type T: Send;
/// Walk the given field.
fn walk_field<'a, E: Effect>(
index: usize,
value: &'ctx Self::T,
visitor: Visitor<'a, 'ctx>,
) -> Future<'a, Result<Flow, Self::FieldError>, E>;
}
#[derive(Debug)]
#[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)]
#[allow(unused)]
pub struct StructWalkError<T> {
kind: StructWalkErrorKind<T>,
}
impl<'ctx, T, I, M, E> StructWalker<'ctx, T, I, M, E>
where
I: StructTypeInfo<'ctx, M>,
{
/// Create walker from a borrow of a struct.
pub fn new(value: &'ctx T) -> Self {
Self {
value,
index: 0,
error: None,
_generics: Default::default(),
}
}
}
impl<'ctx, T, I, M, E> WalkerTypes for StructWalker<'ctx, T, I, M, E>
where
I: StructTypeInfo<'ctx, M>,
{
type Error = StructWalkError<I::FieldError>;
type Output = ();
}
impl<'ctx, T, I, E, M> crate::Walker<'ctx, E> for StructWalker<'ctx, T, I, M, E>
where
E: Effect,
I: StructTypeInfo<'ctx, M, T = T>,
T: Sync + 'static,
{
fn walk<'a>(
mut self,
visitor: Visitor<'a, 'ctx>,
) -> Future<'a, Result<Self::Output, Self::Error>, E>
where
Self: 'a,
{
E::wrap(async {
// Use the recoverable walk to not duplicate the code.
let _ = RecoverableScope::<'ctx, E>::new_walk(&mut self, visitor).await;
// Get the error if there was one.
match self.error {
Some(err) => Err(StructWalkError { kind: err }),
None => Ok(()),
}
})
}
}
any_trait! {
impl['ctx, T, I, M, E] StructWalker<'ctx, T, I, M, E> = [
DynHint<'ctx, DynRecoverable<'ctx, E>, E>,
DynHint<'ctx, DynSequence<'ctx, E>, E>,
DynHint<'ctx, DynValue<'ctx, DynBorrowedStatic<'ctx, T>, E>, E>,
DynHint<'ctx, DynTag<'ctx, TagDyn, E>, E>,
DynHint<'ctx, DynTag<'ctx, TagConst<{ TAG_TYPE_ID.to_int() }>, E>, E>,
DynHint<'ctx, DynTag<'ctx, TagConst<{ TAG_STRUCT.to_int() }>, E>, E>,
DynHint<'ctx, DynTag<'ctx, TagConst<{ TAG_MAP.to_int() }>, E>, E>,
DynHint<'ctx, DynTag<'ctx, TagConst<{ TAG_TYPE_NAME.to_int() }>, E>, E>,
DynHint<'ctx, DynTag<'ctx, TagConst<{ TAG_FIELD_NAMES.to_int() }>, E>, E>,
] where
E: Effect,
T: Sync + 'static,
I: StructTypeInfo<'ctx, M, T = T>
}
impl<'ctx, T, I, M, E> Hint<'ctx, DynRecoverable<'ctx, E>, E> for StructWalker<'ctx, T, I, M, E>
where
E: Effect,
I: StructTypeInfo<'ctx, M, T = T>,
T: Sync + 'static,
{
fn hint<'a>(
&'a mut self,
visitor: Visitor<'a, 'ctx>,
_hint: <DynRecoverable<'ctx, E> as HintMeta<'ctx>>::Hint,
) -> Future<'a, Flow, E> {
E::map(
visit_recoverable::<E>(visitor, self),
|status| match status {
Status::Skipped(_) => Flow::Continue,
Status::Flow(flow) => flow,
},
)
}
fn known<'a>(
&'a mut self,
_hint: &'a <DynRecoverable<'ctx, E> as HintMeta<'ctx>>::Hint,
) -> Future<'a, Result<Known<'a, 'ctx, DynRecoverable<'ctx, E>>, ()>, E> {
E::ready(Ok(()))
}
}
impl<'ctx, T, I, M, E> Hint<'ctx, DynTag<'ctx, TagConst<{ TAG_FIELD_NAMES.to_int() }>, E>, E>
for StructWalker<'ctx, T, I, M, E>
where
E: Effect,
I: StructTypeInfo<'ctx, M>,
T: Sync + 'static,
{
fn hint<'a>(
&'a mut self,
visitor: Visitor<'a, 'ctx>,
_hint: <DynTag<'ctx, TagConst<{ TAG_FIELD_NAMES.to_int() }>, E> as HintMeta<'ctx>>::Hint,
) -> Future<'a, Flow, E> {
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::Break
}
Ok(Status::Skipped(_)) => Flow::Continue,
Ok(Status::Flow(flow)) => flow,
},
)
}
fn known<'a>(
&'a mut self,
_hint: &'a <DynTag<'ctx, TagConst<{ TAG_FIELD_NAMES.to_int() }>, E> as HintMeta<
'ctx,
>>::Hint,
) -> Future<'a,
Result<Known<'a, 'ctx, DynTag<'ctx, TagConst<{ TAG_FIELD_NAMES.to_int() }>, E>>, ()>,
E,
> {
E::ready(Ok(TagKnown {
kind_available: Some(true),
}))
}
}
impl<'ctx, T, I, M, E> Hint<'ctx, DynTag<'ctx, TagConst<{ TAG_TYPE_NAME.to_int() }>, E>, E>
for StructWalker<'ctx, T, I, M, E>
where
E: Effect,
I: StructTypeInfo<'ctx, M>,
T: Sync + 'static,
{
fn hint<'a>(
&'a mut self,
visitor: Visitor<'a, 'ctx>,
_hint: <DynTag<'ctx, TagConst<{ TAG_TYPE_NAME.to_int() }>, E> as HintMeta<'ctx>>::Hint,
) -> Future<'a, Flow, E> {
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::Break
}
Ok(Status::Skipped(_)) => Flow::Continue,
Ok(Status::Flow(flow)) => flow,
},
)
}
fn known<'a>(
&'a mut self,
_hint: &'a <DynTag<'ctx, TagConst<{ TAG_TYPE_NAME.to_int() }>, E> as HintMeta<'ctx>>::Hint,
) -> Future<
'a,
Result<Known<'a, 'ctx, DynTag<'ctx, TagConst<{ TAG_TYPE_NAME.to_int() }>, E>>, ()>,
E,
> {
E::ready(Ok(TagKnown {
kind_available: Some(true),
}))
}
}
impl<'ctx, T, I, M, E> Hint<'ctx, DynTag<'ctx, TagConst<{ TAG_MAP.to_int() }>, E>, E>
for StructWalker<'ctx, T, I, M, E>
where
E: Effect,
I: StructTypeInfo<'ctx, M>,
T: Sync + 'static,
{
fn hint<'a>(
&'a mut self,
visitor: Visitor<'a, 'ctx>,
_hint: <DynTag<'ctx, TagConst<{ TAG_MAP.to_int() }>, E> as HintMeta<'ctx>>::Hint,
) -> Future<'a, Flow, E> {
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::Break
}
Ok(Status::Skipped(_)) => Flow::Continue,
Ok(Status::Flow(flow)) => flow,
},
)
}
fn known<'a>(
&'a mut self,
_hint: &'a <DynTag<'ctx, TagConst<{ TAG_MAP.to_int() }>, E> as HintMeta<'ctx>>::Hint,
) -> Future<
'a,
Result<Known<'a, 'ctx, DynTag<'ctx, TagConst<{ TAG_MAP.to_int() }>, E>>, ()>,
E,
> {
E::ready(Ok(TagKnown {
kind_available: Some(true),
}))
}
}
impl<'ctx, T, I, M, E> Hint<'ctx, DynTag<'ctx, TagConst<{ TAG_STRUCT.to_int() }>, E>, E>
for StructWalker<'ctx, T, I, M, E>
where
E: Effect,
I: StructTypeInfo<'ctx, M>,
T: Sync + 'static,
{
fn hint<'a>(
&'a mut self,
visitor: Visitor<'a, 'ctx>,
_hint: <DynTag<'ctx, TagConst<{ TAG_STRUCT.to_int() }>, E> as HintMeta<'ctx>>::Hint,
) -> Future<'a, Flow, E> {
E::map(
visit_tag::<TagConst<{ TAG_STRUCT.to_int() }>, E, _>(
TagConst,
visitor,
NoopWalker::new(),
),
|status| match status {
Err(err) => {
self.error = Some(StructWalkErrorKind::Tag(err));
Flow::Break
}
Ok(Status::Skipped(_)) => Flow::Continue,
Ok(Status::Flow(flow)) => flow,
},
)
}
fn known<'a>(
&'a mut self,
_hint: &'a <DynTag<'ctx, TagConst<{ TAG_STRUCT.to_int() }>, E> as HintMeta<'ctx>>::Hint,
) -> Future<
'a,
Result<Known<'a, 'ctx, DynTag<'ctx, TagConst<{ TAG_STRUCT.to_int() }>, E>>, ()>,
E,
> {
E::ready(Ok(TagKnown {
kind_available: Some(true),
}))
}
}
impl<'ctx, T, I, M, E> Hint<'ctx, DynTag<'ctx, TagConst<{ TAG_TYPE_ID.to_int() }>, E>, E>
for StructWalker<'ctx, T, I, M, E>
where
E: Effect,
I: StructTypeInfo<'ctx, M>,
T: Sync + 'static,
{
fn hint<'a>(
&'a mut self,
visitor: Visitor<'a, 'ctx>,
_hint: <DynTag<'ctx, TagConst<{ TAG_TYPE_ID.to_int() }>, E> as HintMeta<'ctx>>::Hint,
) -> Future<'a, Flow, E> {
E::map(
visit_tag::<TagConst<{ TAG_TYPE_ID.to_int() }>, E, _>(
TagConst,
visitor,
ValueWalker::new(TypeId::of::<T>()),
),
|status| match status {
Err(err) => {
self.error = Some(StructWalkErrorKind::Tag(err));
Flow::Break
}
Ok(Status::Skipped(_)) => Flow::Continue,
Ok(Status::Flow(flow)) => flow,
},
)
}
fn known<'a>(
&'a mut self,
_hint: &'a <DynTag<'ctx, TagConst<{ TAG_TYPE_ID.to_int() }>, E> as HintMeta<'ctx>>::Hint,
) -> Future<
'a,
Result<Known<'a, 'ctx, DynTag<'ctx, TagConst<{ TAG_TYPE_ID.to_int() }>, E>>, ()>,
E,
> {
E::ready(Ok(TagKnown {
kind_available: Some(true),
}))
}
}
impl<'ctx, T, I, M, E> Hint<'ctx, DynTag<'ctx, TagDyn, E>, E> for StructWalker<'ctx, T, I, M, E>
where
E: Effect,
I: StructTypeInfo<'ctx, M>,
T: Sync + 'static,
{
fn hint<'a>(
&'a mut self,
visitor: Visitor<'a, 'ctx>,
hint: <DynTag<'ctx, TagDyn, E> as HintMeta<'ctx>>::Hint,
) -> Future<'a, Flow, E> {
match hint.kind.0 {
crate::TAG_TYPE_ID => Hint::<
'ctx,
DynTag<'ctx, TagConst<{ TAG_TYPE_ID.to_int() }>, E>,
E,
>::hint(self, visitor, TagHint { kind: TagConst }),
crate::TAG_STRUCT => {
Hint::<'ctx, DynTag<'ctx, TagConst<{ TAG_STRUCT.to_int() }>, E>, E>::hint(
self,
visitor,
TagHint { kind: TagConst },
)
}
crate::TAG_MAP => {
Hint::<'ctx, DynTag<'ctx, TagConst<{ TAG_MAP.to_int() }>, E>, E>::hint(
self,
visitor,
TagHint { kind: TagConst },
)
}
crate::TAG_TYPE_NAME => Hint::<
'ctx,
DynTag<'ctx, TagConst<{ TAG_TYPE_NAME.to_int() }>, E>,
E,
>::hint(self, visitor, TagHint { kind: TagConst }),
crate::TAG_FIELD_NAMES => Hint::<
'ctx,
DynTag<'ctx, TagConst<{ TAG_FIELD_NAMES.to_int() }>, E>,
E,
>::hint(self, visitor, TagHint { kind: TagConst }),
_ => E::ready(Flow::Continue),
}
}
fn known<'a>(
&'a mut self,
hint: &'a <DynTag<'ctx, TagDyn, E> as HintMeta<'ctx>>::Hint,
) -> Future<'a, Result<Known<'a, 'ctx, DynTag<'ctx, 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, T, I, M, E> Hint<'ctx, DynValue<'ctx, DynBorrowedStatic<'ctx, T>, E>, E>
for StructWalker<'ctx, T, I, M, E>
where
E: Effect,
I: StructTypeInfo<'ctx, M>,
T: Sync + 'static,
{
fn hint<'a>(&'a mut self, visitor: Visitor<'a, 'ctx>, _hint: ()) -> Future<'a, Flow, E> {
E::map(
visit_value::<_, E>(visitor, BorrowedStatic(self.value)),
|status| match status {
Status::Skipped(_) => Flow::Continue,
Status::Flow(flow) => flow,
},
)
}
fn known<'a>(&'a mut self, _hint: &'a ()) -> Future<'a, Result<ValueKnown, ()>, E> {
E::ready(Ok(ValueKnown))
}
}
impl<'ctx, T, I, M, E> Hint<'ctx, DynSequence<'ctx, E>, E> for StructWalker<'ctx, T, I, M, E>
where
E: Effect,
I: StructTypeInfo<'ctx, M, T = T>,
T: Sync,
{
fn hint<'a>(
&'a mut self,
visitor: Visitor<'a, 'ctx>,
_hint: <DynSequence<'ctx, E> as HintMeta<'ctx>>::Hint,
) -> Future<'a, Flow, E> {
E::map(visit_sequence::<E>(visitor, self), |status| match status {
Status::Skipped(_) => Flow::Continue,
Status::Flow(flow) => flow,
})
}
fn known<'a>(
&'a mut self,
_hint: &'a <DynSequence<'ctx, E> as HintMeta<'ctx>>::Hint,
) -> Future<'a, Result<Known<'a, 'ctx, DynSequence<'ctx, E>>, ()>, E> {
let len = I::FIELDS.len();
E::ready(Ok(SequenceKnown {
len: (len, Some(len)),
}))
}
}
impl<'ctx, T, I, M, E> SequenceScope<'ctx, E> for StructWalker<'ctx, T, I, M, E>
where
E: Effect,
I: StructTypeInfo<'ctx, M, T = T>,
{
fn size_hint(&mut self) -> Future<'_, (usize, Option<usize>), E> {
let len = I::FIELDS.len();
E::ready((len, Some(len)))
}
fn next<'a>(&'a mut self, visitor: Visitor<'a, 'ctx>) -> Future<'a, Flow, E> {
if self.index >= I::FIELDS.len() {
return E::ready(Flow::Done);
}
let index = self.index;
self.index += 1;
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::Break
}
},
)
}
}
impl<'ctx, T, I, M, E> RecoverableScope<'ctx, E> for StructWalker<'ctx, T, I, M, E>
where
E: Effect,
I: StructTypeInfo<'ctx, M, T = T>,
T: Sync + 'static,
{
fn new_walk<'a>(&'a mut self, visitor: Visitor<'a, 'ctx>) -> Future<'a, Flow, E> {
// Reset the errors to default state.
self.error = None;
// Reset the field index to the default.
self.index = 0;
E::wrap(async move {
// We should check if the visitor wants something specific.
match visit_request_hint::<E>(visitor, self).await {
Flow::Continue => {}
flow => return flow,
}
// Attempt to visit the value directly.
match visit_value::<_, E>(visitor, BorrowedStatic(self.value)).await {
Status::Skipped(_) | Status::Flow(Flow::Continue) => {}
Status::Flow(flow) => return flow,
}
// Follow the standard set of protocols for a struct.
// - Tagged: type ID
// - Tagged: struct
// - Tagged: struct name
// - Tagged: struct field names
// - Sequence: the fields
match visit_tag::<TagConst<{ TAG_TYPE_ID.to_int() }>, E, _>(
TagConst,
visitor,
ValueWalker::new(TypeId::of::<T>()),
)
.await
{
Err(err) => {
self.error = Some(StructWalkErrorKind::Tag(err));
return Flow::Break;
}
Ok(Status::Skipped(_)) | Ok(Status::Flow(Flow::Continue)) => {}
Ok(Status::Flow(flow)) => return flow,
}
match visit_tag::<TagConst<{ TAG_STRUCT.to_int() }>, E, _>(
TagConst,
visitor,
NoopWalker::new(),
)
.await
{
Ok(Status::Skipped(_)) => {
match visit_tag::<TagConst<{ TAG_MAP.to_int() }>, E, _>(
TagConst,
visitor,
NoopWalker::new(),
)
.await
{
Err(err) => {
self.error = Some(StructWalkErrorKind::Tag(err));
return Flow::Break;
}
Ok(Status::Skipped(_)) | Ok(Status::Flow(Flow::Continue)) => {}
Ok(Status::Flow(flow)) => return flow,
}
}
Err(err) => {
self.error = Some(StructWalkErrorKind::Tag(err));
return Flow::Break;
}
Ok(Status::Flow(Flow::Continue)) => {}
Ok(Status::Flow(flow)) => return flow,
}
match visit_tag::<TagConst<{ TAG_TYPE_NAME.to_int() }>, E, _>(
TagConst,
visitor,
ValueWalker::new(I::NAME),
)
.await
{
Err(err) => {
self.error = Some(StructWalkErrorKind::Tag(err));
return Flow::Break;
}
Ok(Status::Skipped(_)) | Ok(Status::Flow(Flow::Continue)) => {}
Ok(Status::Flow(flow)) => return flow,
}
match visit_tag::<TagConst<{ TAG_FIELD_NAMES.to_int() }>, E, _>(
TagConst,
visitor,
StaticSliceWalker::<_, ValueWalker<&'static str>>::new(I::FIELDS),
)
.await
{
Err(err) => {
self.error = Some(StructWalkErrorKind::FieldTag(err));
return Flow::Break;
}
Ok(Status::Skipped(_)) | Ok(Status::Flow(Flow::Continue)) => {}
Ok(Status::Flow(flow)) => return flow,
}
match visit_sequence::<E>(visitor, self).await {
Status::Flow(Flow::Continue) | Status::Skipped(_) => {}
Status::Flow(flow) => return flow,
}
Flow::Continue
})
}
}