Diffstat (limited to 'src/walk/walkers/core/struct.rs')
| -rw-r--r-- | src/walk/walkers/core/struct.rs | 647 |
1 files changed, 647 insertions, 0 deletions
diff --git a/src/walk/walkers/core/struct.rs b/src/walk/walkers/core/struct.rs new file mode 100644 index 0000000..a07557f --- /dev/null +++ b/src/walk/walkers/core/struct.rs @@ -0,0 +1,647 @@ +use core::{any::TypeId, marker::PhantomData}; + +use crate::{ + any::static_wrapper::BorrowedStatic, + any_trait, + effect::{Effect, Future}, + 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}, + 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>>, + + _marker: PhantomData<fn() -> (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<'ctx>>( + index: usize, + value: &'ctx Self::T, + visitor: Visitor<'a, 'ctx>, + ) -> Future<'a, 'ctx, Result<Flow, Self::FieldError>, E>; +} + +#[derive(Debug)] +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)] +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, + _marker: PhantomData, + } + } +} + +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<'ctx>, + I: StructTypeInfo<'ctx, M, T = T>, + T: Sync + 'static, +{ + fn walk<'a>( + mut self, + visitor: Visitor<'a, 'ctx>, + ) -> Future<'a, 'ctx, 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['a, 'ctx, T, I, M, E] StructWalker<'ctx, T, I, M, E> = [ + DynHint<'a, 'ctx, DynRecoverable<'a, 'ctx, E>, E>, + DynHint<'a, 'ctx, DynSequence<'a, 'ctx, E>, E>, + DynHint<'a, 'ctx, DynValue<'a, 'ctx, BorrowedStatic<'ctx, T>, E>, E>, + DynHint<'a, 'ctx, DynTag<'a, 'ctx, TagDyn, E>, E>, + DynHint<'a, 'ctx, DynTag<'a, 'ctx, TagConst<{ TAG_TYPE_ID.to_int() }>, E>, E>, + DynHint<'a, 'ctx, DynTag<'a, 'ctx, TagConst<{ TAG_STRUCT.to_int() }>, E>, E>, + DynHint<'a, 'ctx, DynTag<'a, 'ctx, TagConst<{ TAG_MAP.to_int() }>, E>, E>, + DynHint<'a, 'ctx, DynTag<'a, 'ctx, TagConst<{ TAG_TYPE_NAME.to_int() }>, E>, E>, + DynHint<'a, 'ctx, DynTag<'a, 'ctx, TagConst<{ TAG_FIELD_NAMES.to_int() }>, E>, E>, + ] where + E: Effect<'ctx>, + T: Sync + 'static, + I: StructTypeInfo<'ctx, M, T = T> +} + +impl<'a, 'ctx: 'a, T, I, M, E> Hint<'a, 'ctx, DynRecoverable<'a, 'ctx, E>, E> + for StructWalker<'ctx, T, I, M, E> +where + E: Effect<'ctx>, + I: StructTypeInfo<'ctx, M, T = T>, + T: Sync + 'static, +{ + fn hint( + &'a mut self, + visitor: Visitor<'a, 'ctx>, + _hint: <DynRecoverable<'a, 'ctx, E> as HintMeta<'ctx>>::Hint, + ) -> Future<'a, 'ctx, Flow, E> { + E::map( + visit_recoverable::<E>(visitor, self), + |status| match status { + Status::Skipped(_) => Flow::Continue, + Status::Flow(flow) => flow, + }, + ) + } + + fn known( + &'a mut self, + _hint: &'a <DynRecoverable<'a, 'ctx, E> as HintMeta<'ctx>>::Hint, + ) -> Future<'a, 'ctx, Result<Known<'a, 'ctx, DynRecoverable<'a, 'ctx, E>>, ()>, E> { + E::ready(Ok(())) + } +} + +impl<'a, 'ctx: 'a, T, I, M, E> + Hint<'a, 'ctx, DynTag<'a, 'ctx, TagConst<{ TAG_FIELD_NAMES.to_int() }>, E>, E> + for StructWalker<'ctx, T, I, M, E> +where + E: Effect<'ctx>, + I: StructTypeInfo<'ctx, M>, + T: Sync + 'static, +{ + fn hint( + &'a mut self, + visitor: Visitor<'a, 'ctx>, + _hint: <DynTag<'a, 'ctx, TagConst<{ TAG_FIELD_NAMES.to_int() }>, E> as HintMeta<'ctx>>::Hint, + ) -> Future<'a, 'ctx, 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 mut self, + _hint: &'a <DynTag<'a, 'ctx, TagConst<{ TAG_FIELD_NAMES.to_int() }>, E> as HintMeta< + 'ctx, + >>::Hint, + ) -> Future< + 'a, + 'ctx, + Result<Known<'a, 'ctx, DynTag<'a, 'ctx, TagConst<{ TAG_FIELD_NAMES.to_int() }>, E>>, ()>, + E, + > { + E::ready(Ok(TagKnown { + kind_available: Some(true), + })) + } +} + +impl<'a, 'ctx: 'a, T, I, M, E> + Hint<'a, 'ctx, DynTag<'a, 'ctx, TagConst<{ TAG_TYPE_NAME.to_int() }>, E>, E> + for StructWalker<'ctx, T, I, M, E> +where + E: Effect<'ctx>, + I: StructTypeInfo<'ctx, M>, + T: Sync + 'static, +{ + fn hint( + &'a mut self, + visitor: Visitor<'a, 'ctx>, + _hint: <DynTag<'a, 'ctx, TagConst<{ TAG_TYPE_NAME.to_int() }>, E> as HintMeta<'ctx>>::Hint, + ) -> Future<'a, 'ctx, 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 mut self, + _hint: &'a <DynTag<'a, 'ctx, TagConst<{ TAG_TYPE_NAME.to_int() }>, E> as HintMeta<'ctx>>::Hint, + ) -> Future< + 'a, + 'ctx, + Result<Known<'a, 'ctx, DynTag<'a, 'ctx, TagConst<{ TAG_TYPE_NAME.to_int() }>, E>>, ()>, + E, + > { + E::ready(Ok(TagKnown { + kind_available: Some(true), + })) + } +} + +impl<'a, 'ctx: 'a, T, I, M, E> + Hint<'a, 'ctx, DynTag<'a, 'ctx, TagConst<{ TAG_MAP.to_int() }>, E>, E> + for StructWalker<'ctx, T, I, M, E> +where + E: Effect<'ctx>, + I: StructTypeInfo<'ctx, M>, + T: Sync + 'static, +{ + fn hint( + &'a mut self, + visitor: Visitor<'a, 'ctx>, + _hint: <DynTag<'a, 'ctx, TagConst<{ TAG_MAP.to_int() }>, E> as HintMeta<'ctx>>::Hint, + ) -> Future<'a, 'ctx, 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 mut self, + _hint: &'a <DynTag<'a, 'ctx, TagConst<{ TAG_MAP.to_int() }>, E> as HintMeta<'ctx>>::Hint, + ) -> Future< + 'a, + 'ctx, + Result<Known<'a, 'ctx, DynTag<'a, 'ctx, TagConst<{ TAG_MAP.to_int() }>, E>>, ()>, + E, + > { + E::ready(Ok(TagKnown { + kind_available: Some(true), + })) + } +} + +impl<'a, 'ctx: 'a, T, I, M, E> + Hint<'a, 'ctx, DynTag<'a, 'ctx, TagConst<{ TAG_STRUCT.to_int() }>, E>, E> + for StructWalker<'ctx, T, I, M, E> +where + E: Effect<'ctx>, + I: StructTypeInfo<'ctx, M>, + T: Sync + 'static, +{ + fn hint( + &'a mut self, + visitor: Visitor<'a, 'ctx>, + _hint: <DynTag<'a, 'ctx, TagConst<{ TAG_STRUCT.to_int() }>, E> as HintMeta<'ctx>>::Hint, + ) -> Future<'a, 'ctx, 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 mut self, + _hint: &'a <DynTag<'a, 'ctx, TagConst<{ TAG_STRUCT.to_int() }>, E> as HintMeta<'ctx>>::Hint, + ) -> Future< + 'a, + 'ctx, + Result<Known<'a, 'ctx, DynTag<'a, 'ctx, TagConst<{ TAG_STRUCT.to_int() }>, E>>, ()>, + E, + > { + E::ready(Ok(TagKnown { + kind_available: Some(true), + })) + } +} + +impl<'a, 'ctx: 'a, T, I, M, E> + Hint<'a, 'ctx, DynTag<'a, 'ctx, TagConst<{ TAG_TYPE_ID.to_int() }>, E>, E> + for StructWalker<'ctx, T, I, M, E> +where + E: Effect<'ctx>, + I: StructTypeInfo<'ctx, M>, + T: Sync + 'static, +{ + fn hint( + &'a mut self, + visitor: Visitor<'a, 'ctx>, + _hint: <DynTag<'a, 'ctx, TagConst<{ TAG_TYPE_ID.to_int() }>, E> as HintMeta<'ctx>>::Hint, + ) -> Future<'a, 'ctx, 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 mut self, + _hint: &'a <DynTag<'a, 'ctx, TagConst<{ TAG_TYPE_ID.to_int() }>, E> as HintMeta<'ctx>>::Hint, + ) -> Future< + 'a, + 'ctx, + Result<Known<'a, 'ctx, DynTag<'a, 'ctx, TagConst<{ TAG_TYPE_ID.to_int() }>, E>>, ()>, + E, + > { + E::ready(Ok(TagKnown { + kind_available: Some(true), + })) + } +} + +impl<'a, 'ctx: 'a, T, I, M, E> Hint<'a, 'ctx, DynTag<'a, 'ctx, TagDyn, E>, E> + for StructWalker<'ctx, T, I, M, E> +where + E: Effect<'ctx>, + I: StructTypeInfo<'ctx, M>, + T: Sync + 'static, +{ + fn hint( + &'a mut self, + visitor: Visitor<'a, 'ctx>, + hint: <DynTag<'a, 'ctx, TagDyn, E> as HintMeta<'ctx>>::Hint, + ) -> Future<'a, 'ctx, Flow, E> { + match hint.kind.0 { + crate::TAG_TYPE_ID => Hint::< + 'a, + 'ctx, + DynTag<'a, 'ctx, TagConst<{ TAG_TYPE_ID.to_int() }>, E>, + E, + >::hint(self, visitor, TagHint { kind: TagConst }), + crate::TAG_STRUCT => Hint::< + 'a, + 'ctx, + DynTag<'a, 'ctx, TagConst<{ TAG_STRUCT.to_int() }>, E>, + E, + >::hint(self, visitor, TagHint { kind: TagConst }), + crate::TAG_MAP => Hint::< + 'a, + 'ctx, + DynTag<'a, 'ctx, TagConst<{ TAG_MAP.to_int() }>, E>, + E, + >::hint(self, visitor, TagHint { kind: TagConst }), + crate::TAG_TYPE_NAME => Hint::< + 'a, + 'ctx, + DynTag<'a, 'ctx, TagConst<{ TAG_TYPE_NAME.to_int() }>, E>, + E, + >::hint(self, visitor, TagHint { kind: TagConst }), + crate::TAG_FIELD_NAMES => Hint::< + 'a, + 'ctx, + DynTag<'a, 'ctx, TagConst<{ TAG_FIELD_NAMES.to_int() }>, E>, + E, + >::hint(self, visitor, TagHint { kind: TagConst }), + _ => E::ready(Flow::Continue), + } + } + + fn known( + &'a mut self, + hint: &'a <DynTag<'a, 'ctx, TagDyn, E> as HintMeta<'ctx>>::Hint, + ) -> Future<'a, 'ctx, Result<Known<'a, 'ctx, DynTag<'a, '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<'a, 'ctx: 'a, T, I, M, E> Hint<'a, 'ctx, DynValue<'a, 'ctx, BorrowedStatic<'ctx, T>, E>, E> + for StructWalker<'ctx, T, I, M, E> +where + E: Effect<'ctx>, + I: StructTypeInfo<'ctx, M>, + T: Sync + 'static, +{ + fn hint(&'a mut self, visitor: Visitor<'a, 'ctx>, _hint: ()) -> Future<'a, 'ctx, 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 mut self, _hint: &'a ()) -> Future<'a, 'ctx, Result<(), ()>, E> { + E::ready(Ok(())) + } +} + +impl<'a, 'ctx: 'a, T, I, M, E> Hint<'a, 'ctx, DynSequence<'a, 'ctx, E>, E> + for StructWalker<'ctx, T, I, M, E> +where + E: Effect<'ctx>, + I: StructTypeInfo<'ctx, M, T = T>, + T: Sync, +{ + fn hint( + &'a mut self, + visitor: Visitor<'a, 'ctx>, + _hint: <DynSequence<'a, 'ctx, E> as HintMeta<'ctx>>::Hint, + ) -> Future<'a, 'ctx, Flow, E> { + E::map(visit_sequence::<E>(visitor, self), |status| match status { + Status::Skipped(_) => Flow::Continue, + Status::Flow(flow) => flow, + }) + } + + fn known( + &'a mut self, + _hint: &'a <DynSequence<'a, 'ctx, E> as HintMeta<'ctx>>::Hint, + ) -> Future<'a, 'ctx, Result<Known<'a, 'ctx, DynSequence<'a, '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<'ctx>, + I: StructTypeInfo<'ctx, M, T = T>, +{ + fn size_hint<'a>(&'a mut self) -> Future<'a, 'ctx, (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, 'ctx, 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<'ctx>, + I: StructTypeInfo<'ctx, M, T = T>, + T: Sync + 'static, +{ + fn new_walk<'a>(&'a mut self, visitor: Visitor<'a, 'ctx>) -> Future<'a, 'ctx, 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 + }) + } +} |