use core::any::TypeId; use effectful::{ bound::Dynamic, effective::{Effective, Canonical}, environment::{Environment}, higher_ranked::{for_lt, Rank1, WithLt}, SendSync, DynBind, }; use crate::{ any::{type_name, AnyTrait, BorrowedStatic, OwnedStatic}, hkt::Marker, protocol::{ visitor::{ request_hint, tags, visit_sequence, visit_tag, visit_value, EffectiveVisitExt as _, Recoverable, RecoverableScope, Sequence, SequenceHint, SequenceKnown, SequenceScope, Tag, TagConst, TagDyn, TagError, TagHint, TagKnown, Value, ValueKnown, VisitResult, }, walker::hint::{DynVisitorWith, Hint, HintMeta}, DynVisitor, DynWalker, }, walk::DynWalkerAdapter, Flow, Never, Status, }; use super::{noop::NoopWalker, value::ValueWalker}; /// Walker for a borrow of a struct. /// /// This walker implements the struct flow. The struct cannot contain lifetimes. #[derive(SendSync)] pub struct StructWalker<'ctx, I: StructTypeInfo<'ctx, M, E, S = S>, S, M, E: Environment> { /// Struct value to walk. value: Dynamic<&'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>, _generics: Marker, } /// Type info about a struct needed by [`StructWalker`]. pub trait StructTypeInfo<'ctx, M, E: Environment>: '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: DynBind + core::fmt::Debug; type S: 'static; /// The struct being described. type T: DynBind; /// Walk the given field. fn walk_field<'a, 'lt>( index: usize, value: &'ctx Self::T, visitor: DynVisitor<'a, 'lt, 'ctx, E>, ) -> Canonical<'a, Result, E>; } #[derive(Debug, PartialEq, Clone, Copy, SendSync)] #[allow(unused)] enum StructWalkErrorKind { /// Error with visiting a tag for the struct. /// /// This error shouldn't really happen if the visitor behaves. Tag(TagError), /// Error with visiting a tag for a struct's field. /// /// This error shouldn't really happen if the visitor behaves. FieldTag(TagError>), /// Error with visiting a field. Field(T), } /// Error from walking a struct. #[derive(Debug, PartialEq, Copy, Clone, SendSync)] #[allow(unused)] pub struct StructWalkError { kind: StructWalkErrorKind, } impl<'ctx, I, S, M, E: Environment> 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: Dynamic(value), index: 0, error: None, _generics: Default::default(), } } fn record_tag_error( &mut self, result: Result, TagError>, ) -> VisitResult { result.unwrap_or_else(|err| { self.error = Some(StructWalkErrorKind::Tag(err)); Flow::Err.into() }) } } impl<'lt, 'ctx, I, S, E, M> crate::Walker<'ctx, E> for StructWalker<'ctx, I, S, M, E> where E: Environment, I: StructTypeInfo<'ctx, M, E, S = S>, Self: AnyTrait<'lt, 'ctx> + RecoverableScope<'ctx, E>, { type Error = StructWalkError; type Output = (); #[inline(always)] fn walk<'b: 'c, 'd: 'c, 'c>( self, visitor: DynVisitor<'b, 'd, 'ctx, E>, ) -> Canonical<'c, Result, E> where Self: 'c, { E::value((self, visitor)) .update_map((), |_, (this, visitor)| { RecoverableScope::<'ctx, E>::new_walk::<'_, '_, '_, '_>(this, visitor.cast()).cast() }) .map((), |_, ((this, _), _)| match this.error { Some(err) => Err(StructWalkError { kind: err }), None => Ok(()), }) .cast() // 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(()), // } // }) } } pub enum StaticType {} impl<'lt, 'ctx: 'lt, I, M: 'lt, E> AnyTrait<'lt, 'ctx> for StructWalker<'ctx, I, StaticType, M, E> where E: Environment, I: StructTypeInfo<'ctx, M, E, S = StaticType>, M: 'ctx, I::T: 'static, Dynamic<&'ctx I::T>: DynBind, Dynamic>: DynBind, for<'a, 'b> Dynamic<&'a BorrowedStatic<'b, I::T>>: DynBind, Dynamic: DynBind, Dynamic>: DynBind, { } // any_trait! { // impl['ctx, I, M][E] StructWalker<'ctx, I, StaticType, M, E> = [ // HintProto>, // HintProto>, // HintProto, E>>, // HintProto>, // HintProto>, // HintProto>, // HintProto>, // HintProto>, // HintProto>, // ] where // } impl<'lt2, 'ctx, I, S, M, E> Hint<'ctx, type_name::Raised<'static, 'ctx, dyn Recoverable<'ctx, E>>> for StructWalker<'ctx, I, S, M, E> where E: Environment, I: StructTypeInfo<'ctx, M, E, S = S>, Self: AnyTrait<'lt2, 'ctx> + RecoverableScope<'ctx, E>, { #[inline(always)] fn hint<'this, 'visitor, 'lt: 'e, 'hint, 'e>( &'this mut self, _visitor: DynVisitorWith< 'visitor, 'lt, 'ctx, type_name::Raised<'static, 'ctx, dyn Recoverable<'ctx, E>>, >, _hint: (), ) -> Canonical<'e, VisitResult, E> where 'ctx: 'this + 'visitor + 'hint + 'e, { todo!() // E::map( // visit_recoverable::(visitor, self), // |status| match status { // VisitResult::Skipped(_) => Flow::Continue, // VisitResult::Control(flow) => flow, // }, // ) } fn known<'a>( &'a mut self, hint: &'a WithLt<'a, Rank1<()>>, ) -> Canonical<'a, Result>, ()>, E> where WithLt<'a, Rank1<()>>: DynBind, { E::value(Ok(())).cast() } } // impl<'c, 'ctx: 'c, I, S, M, E> Hint<'ctx, dyn Tag<'ctx, tags::FieldNames, E> + 'c> // for StructWalker<'ctx, I, S, M, E> // where // Self: DynBind, // E: Environment, // I: StructTypeInfo<'ctx, M, E, S = S>, // { // #[inline(always)] // fn hint<'this, 'visitor, 'hint, 'e>( // &'this mut self, // _visitor: DynVisitorWith< // 'visitor, // 'ctx, // dyn Tag<'ctx, tags::FieldNames, E> + 'c, // >, // _hint: TagHint, // ) -> Canonical<'e, VisitResult, E> // where // 'ctx: 'this + 'visitor + 'hint + 'e, // { // todo!() // // E::map( // // visit_tag::, 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 WithLt<'a, Rank1<()>>, // ) -> Canonical<'a, Result>, ()>, Protocol::Env> // where // WithLt<'a, Rank1>: DynBind // { // E::value(Ok(TagKnown { // kind_available: Some(true), // })) // .cast() // } // } // // impl<'c, 'ctx: 'c, I, S, M, E> Hint<'ctx, dyn Tag<'ctx, tags::TypeName, E> + 'c> // for StructWalker<'ctx, I, S, M, E> // where // Self: DynBind, // E: Environment, // I: StructTypeInfo<'ctx, M, E, S = S>, // { // #[inline(always)] // fn hint<'this, 'visitor, 'hint, 'e>( // &'this mut self, // _visitor: DynVisitorWith<'visitor, 'ctx, dyn Tag<'ctx, tags::TypeName, E> + 'c>, // _hint: TagHint, // ) -> Canonical<'e, VisitResult, E> // where // 'ctx: 'this + 'visitor + 'hint + 'e, // { // todo!() // // E::map( // // visit_tag::, 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 WithLt<'a, Rank1<()>>, // ) -> Canonical<'a, Result>, ()>, Protocol::Env> // where // WithLt<'a, Rank1>: DynBind // { // E::value(Ok(TagKnown { // kind_available: Some(true), // })) // .cast() // } // } // // impl<'c, 'ctx: 'c, I, S, M, E> Hint<'ctx, dyn Tag<'ctx, tags::Map, E> + 'c> // for StructWalker<'ctx, I, S, M, E> // where // Self: DynBind, // E: Environment, // I: StructTypeInfo<'ctx, M, E, S = S>, // { // #[inline(always)] // fn hint<'this, 'visitor, 'hint, 'e>( // &'this mut self, // _visitor: DynVisitorWith<'visitor, 'ctx, dyn Tag<'ctx, tags::Map, E> + 'c>, // _hint: TagHint, // ) -> Canonical<'e, VisitResult, E> // where // 'ctx: 'this + 'visitor + 'hint + 'e, // { // todo!() // // E::map( // // visit_tag::, 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 WithLt<'a, Rank1<()>>, // ) -> Canonical<'a, Result>, ()>, Protocol::Env> // where // WithLt<'a, Rank1>: DynBind // { // E::value(Ok(TagKnown { // kind_available: Some(true), // })) // .cast() // } // } // // impl<'c, 'ctx: 'c, I, S, M, E> Hint<'ctx, dyn Tag<'ctx, tags::Struct, E> + 'c> // for StructWalker<'ctx, I, S, M, E> // where // Self: DynBind, // E: Environment, // I: StructTypeInfo<'ctx, M, E, S = S>, // { // #[inline(always)] // fn hint<'this: 'e, 'visitor: 'e, 'hint: 'e, 'e>( // &'this mut self, // visitor: DynVisitorWith<'visitor, 'ctx, dyn Tag<'ctx, tags::Struct, E> + 'c>, // _hint: TagHint, // ) -> Canonical<'e, VisitResult, E> // where // 'ctx: 'this + 'visitor + 'hint + 'e, // { // E::with( // (visitor, DynWalkerAdapter::new(NoopWalker::new())), // |(visitor, noop_walker)| { // visitor // .as_known() // .visit(TagConst, noop_walker) // .map((), |_, status| { // VisitResult::Control(status.to_flow().unwrap_or(Flow::Continue)) // }) // .cast() // }, // ) // .map((), |_, (_, x)| x) // .cast() // } // // #[inline(always)] // fn known<'a>( // &'a mut self, // hint: &'a WithLt<'a, Rank1<()>>, // ) -> Canonical<'a, Result>, ()>, Protocol::Env> // where // WithLt<'a, Rank1>: DynBind // { // E::value(Ok(TagKnown { // kind_available: Some(true), // })) // .cast() // } // } // // impl<'c, 'ctx: 'c, I, M, E> Hint<'ctx, dyn Tag<'ctx, tags::TypeId, E> + 'c> // for StructWalker<'ctx, I, StaticType, M, E> // where // Self: DynBind, // E: Environment, // I: StructTypeInfo<'ctx, M, E, S = StaticType>, // I::T: 'static, // { // #[inline(always)] // fn hint<'this, 'visitor, 'hint, 'e>( // &'this mut self, // _visitor: DynVisitorWith<'visitor, 'ctx, dyn Tag<'ctx, tags::TypeId, E> + 'c>, // _hint: TagHint, // ) -> Canonical<'e, VisitResult, E> // where // 'ctx: 'this + 'visitor + 'hint + 'e, // { // todo!() // // E::map( // // visit_tag::, E, _>( // // TagConst, // // visitor, // // ValueWalker::new(TypeId::of::()), // // ), // // |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 WithLt<'a, Rank1<()>>, // ) -> Canonical<'a, Result>, ()>, Protocol::Env> // where // WithLt<'a, Rank1>: DynBind // { // E::value(Ok(TagKnown { // kind_available: Some(true), // })) // .cast() // } // } // // impl<'c, 'ctx: 'c, I, M, E> Hint<'ctx, dyn Tag<'ctx, TagDyn, E> + 'c> for StructWalker<'ctx, I, StaticType, M, E> // where // Self: DynBind, // E: Environment, // 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: DynVisitorWith<'visitor, 'ctx, dyn Tag<'ctx, TagDyn, E> + 'c>, // _hint: TagHint, // ) -> Canonical<'e, VisitResult, E> // where // 'ctx: 'this + 'visitor + 'hint + 'e, // { // todo!() // // match hint.kind.0 { // // crate::TAG_TYPE_ID => { // // Hint::<'ctx, TagProto, E>>::hint( // // self, // // visitor, // // TagHint { kind: TagConst }, // // ) // // } // // crate::TAG_STRUCT => { // // Hint::<'ctx, TagProto, E>>::hint( // // self, // // visitor, // // TagHint { kind: TagConst }, // // ) // // } // // crate::TAG_MAP => Hint::<'ctx, TagProto, E>>::hint( // // self, // // visitor, // // TagHint { kind: TagConst }, // // ), // // crate::TAG_TYPE_NAME => { // // Hint::<'ctx, TagProto, E>>::hint( // // self, // // visitor, // // TagHint { kind: TagConst }, // // ) // // } // // crate::TAG_FIELD_NAMES => Hint::< // // 'ctx, // // TagProto, E>, // // >::hint(self, visitor, TagHint { kind: TagConst }), // // _ => E::ready(Flow::Continue), // // } // } // // #[inline(always)] // fn known<'a>( // &'a mut self, // hint: &'a WithLt<'a, Rank1>>, // ) -> Canonical<'a, Result>, ()>, Protocol::Env> // where // WithLt<'a, Rank1>: DynBind // { // E::value(match hint.kind { // TagDyn(tags::TypeId::VALUE) | TagDyn(tags::Struct::VALUE) => Ok(TagKnown { // kind_available: Some(true), // }), // _ => Ok(TagKnown { // kind_available: Some(false), // }), // }) // .cast() // } // } // // impl<'x, 'ctx: 'x, I, M, E> Hint<'ctx, dyn Value<'ctx, BorrowedStatic<'ctx, I::T>, E> + 'x> // for StructWalker<'ctx, I, StaticType, M, E> // where // E: Environment, // I: StructTypeInfo<'ctx, M, E, S = StaticType>, // I::T: 'static, // for<'a, 'b> Dynamic<&'a BorrowedStatic<'b, I::T>>: DynBind, // Dynamic<&'ctx I::T>: DynBind, // { // #[inline(always)] // fn hint<'this, 'visitor, 'hint, 'e>( // &'this mut self, // _visitor: DynVisitorWith<'visitor, 'ctx, dyn Value<'ctx, BorrowedStatic<'ctx, I::T>, E> + 'x>, // _hint: (), // ) -> Canonical<'e, VisitResult, 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 WithLt<'a, Rank1<()>>, // ) -> Canonical<'a, Result>>>, ()>, Protocol::Env> // where // WithLt<'a, Rank1>>>: DynBind // { // E::value(Ok(ValueKnown { preview: None })).cast() // } // } impl<'ctx, I, S, M: 'ctx, E> Hint<'ctx, type_name::Raised<'static, 'ctx, dyn Sequence<'ctx, E>>> for StructWalker<'ctx, I, S, M, E> where Self: DynBind, E: Environment, I: StructTypeInfo<'ctx, M, E, S = S>, { #[inline(always)] fn hint<'this: 'e, 'visitor: 'e, 'lt: 'e, 'hint: 'e, 'e>( &'this mut self, visitor: DynVisitorWith< 'visitor, 'lt, 'ctx, type_name::Raised<'static, 'ctx, dyn Sequence<'ctx, E>>, >, _hint: SequenceHint, ) -> Canonical<'e, VisitResult, E> where 'ctx: 'this + 'visitor + 'hint + 'e, { E::value((self, visitor)) .update_map((), |_, (this, visitor)| { visitor .as_known() .visit(*this) .map((), |_, status| { match status { VisitResult::Skipped(_) => Flow::Continue, VisitResult::Control(flow) => flow, } .into() }) .cast() }) .map((), |_, (_, x)| x) .cast() } #[inline(always)] fn known<'a>( &'a mut self, hint: &'a WithLt<'a, Rank1>, ) -> Canonical<'a, Result>, ()>, E> where WithLt<'a, Rank1>: DynBind, { let len = I::FIELDS.len(); E::value(Ok(SequenceKnown { len: (len, Some(len)), })) .cast() } } impl<'ctx, I, S, M: 'ctx, E> SequenceScope<'ctx, E> for StructWalker<'ctx, I, S, M, E> where Self: DynBind, E: Environment, I: StructTypeInfo<'ctx, M, E, S = S>, { #[inline(always)] fn size_hint(&mut self) -> Canonical<'_, (usize, Option), E> { let len = I::FIELDS.len(); E::value((len, Some(len))).cast() } #[inline(always)] fn next<'a: 'c, 'd: 'c, 'b: 'c, 'c>( &'a mut self, visitor: DynVisitor<'b, 'd, 'ctx, E>, ) -> Canonical<'c, Flow, E> { if self.index >= I::FIELDS.len() { return E::value(Flow::Done).cast(); } let index = self.index; self.index += 1; I::walk_field(index, self.value.0, visitor) .map(self, |this, result| match result { Ok(flow) => flow, Err(err) => { // Record the error and signal a break. this.error = Some(StructWalkErrorKind::Field(err)); Flow::Err } }) .cast() // E::map( // I::walk_field::(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: Environment, I: StructTypeInfo<'ctx, M, E, S = StaticType>, I::T: 'static, Dynamic>: DynBind, for<'a, 'b> Dynamic<&'a BorrowedStatic<'b, I::T>>: DynBind, Dynamic<&'ctx I::T>: DynBind, Dynamic: DynBind, Dynamic>: DynBind, { #[inline(always)] fn new_walk<'a: 'c, 'b: 'c, 'd: 'c, 'c>( &'a mut self, visitor: DynVisitor<'b, 'd, 'ctx, E>, ) -> Canonical<'c, Status, E> { // Reset the errors to default state. self.error = None; // Reset the field index to the default. self.index = 0; E::value((self, visitor)) .update_map((), |_, (this, visitor)| { request_hint::(visitor.cast(), DynWalker(*this)) .map((), |_, x| VisitResult::unit_skipped(x)) .cast() }) .cast::<()>() .if_not_finished((), |_, (this, visitor)| { visit_value::<_, E>(visitor.cast(), BorrowedStatic(this.value.0)) .map((), |_, x| VisitResult::unit_skipped(x)) .cast() }) .cast::<()>() .if_not_finished((), |_, (this, visitor)| { visit_tag::( TagConst, visitor.cast(), ValueWalker::new(TypeId::of::()), ) .map(this, |this, result| { this.record_tag_error(result).unit_skipped() }) .cast() }) .cast::<()>() .if_not_finished((), |_, (this, visitor)| { visit_tag::(TagConst, visitor.cast(), NoopWalker::new()) .map(this, |this, result| { this.record_tag_error(result).unit_skipped() }) .cast() }) .cast::<()>() .if_skipped((), |_, (this, visitor)| { visit_tag::(TagConst, visitor.cast(), NoopWalker::new()) .map(this, |this, result| { this.record_tag_error(result).unit_skipped() }) .cast() }) .cast::<()>() .if_not_finished((), |_, (this, visitor)| { visit_sequence::(visitor.cast(), *this) .map((), |_, x| VisitResult::unit_skipped(x)) .cast() }) .map((), |_, (_, x)| VisitResult::to_status(x)) .cast() } }