added struct walker
| -rw-r--r-- | Cargo.toml | 4 | ||||
| -rw-r--r-- | empty.rs | 0 | ||||
| -rw-r--r-- | src/build/builders/debug.rs | 86 | ||||
| -rw-r--r-- | src/lib.rs | 244 | ||||
| -rw-r--r-- | src/macros.rs | 1 | ||||
| -rw-r--r-- | src/protocol/visitor.rs | 40 | ||||
| -rw-r--r-- | src/protocol/visitor/recoverable.rs | 73 | ||||
| -rw-r--r-- | src/protocol/visitor/request_hint.rs | 25 | ||||
| -rw-r--r-- | src/protocol/visitor/sequence.rs | 16 | ||||
| -rw-r--r-- | src/protocol/visitor/tag.rs | 170 | ||||
| -rw-r--r-- | src/protocol/visitor/value.rs | 11 | ||||
| -rw-r--r-- | src/protocol/walker/hint.rs | 39 | ||||
| -rw-r--r-- | src/walk.rs | 24 | ||||
| -rw-r--r-- | src/walk/walkers/core.rs | 2 | ||||
| -rw-r--r-- | src/walk/walkers/core/bool.rs | 2 | ||||
| -rw-r--r-- | src/walk/walkers/core/key_value.rs | 92 | ||||
| -rw-r--r-- | src/walk/walkers/core/struct.rs | 647 | ||||
| -rw-r--r-- | src/walk/walkers/core/tag.rs | 17 |
18 files changed, 1126 insertions, 367 deletions
@@ -9,10 +9,6 @@ readme = "README.md" include = ["LICENSE-APACHE", "LICENSE-MIT", "README.md", "empty.rs"] -[lib] -name = "treaty" -path = "empty.rs" - [dependencies] serde = { version = "1.0", default-features = false, optional = true } diff --git a/empty.rs b/empty.rs deleted file mode 100644 index e69de29..0000000 --- a/empty.rs +++ /dev/null diff --git a/src/build/builders/debug.rs b/src/build/builders/debug.rs index 4a51281..cd144d8 100644 --- a/src/build/builders/debug.rs +++ b/src/build/builders/debug.rs @@ -10,8 +10,8 @@ use crate::{ visitor::{ request_hint::{DynRequestHint, RequestHint}, sequence::{DynSequence, Sequence}, - tag::{Dyn, DynTag, Tag, DynFlow}, - value::{DynValue, Value}, + tag::{DynTag, Tag, TagDyn}, + value::{DynValue, Value}, Status, }, }, DynWalker, Flow, @@ -22,7 +22,8 @@ pub struct Visitor<E>(usize, PhantomData<fn() -> E>); any_trait! { impl['a, 'ctx, E] Visitor<E> = [ DynRequestHint<'a, 'ctx, E>, - DynTag<'a, 'ctx, Dyn, E>, + // DynRecoverable<'a, 'ctx, E>, + DynTag<'a, 'ctx, TagDyn, E>, DynValue<'a, 'ctx, OwnedStatic<&'static str>, E>, DynValue<'a, 'ctx, OwnedStatic<TypeId>, E>, DynValue<'a, 'ctx, OwnedStatic<usize>, E>, @@ -42,8 +43,11 @@ impl<'ctx, E: Effect<'ctx>> Visitor<E> { } fn tab(&self) { - for _ in 0..self.0 { - print!(" "); + if self.0 > 0 { + for _ in 0..self.0 - 1 { + print!(" | "); + } + print!(" |-"); } } } @@ -53,27 +57,53 @@ impl<'ctx, E: Effect<'ctx>> RequestHint<'ctx, E> for Visitor<E> { &'a mut self, _walker: crate::protocol::Walker<'a, 'ctx>, ) -> Future<'a, 'ctx, Flow, E> { - self.tab(); - println!("Visit request hint (no hint given)"); + // self.tab(); + // println!("Visit request hint (no hint given)"); + // println!("Visit request hint (hint for recoverable)"); E::ready(Flow::Continue) } } -impl<'ctx, E: Effect<'ctx>> Tag<'ctx, Dyn, E> for Visitor<E> { +impl<'ctx, E: Effect<'ctx>> Tag<'ctx, TagDyn, E> for Visitor<E> { fn visit<'a>( &'a mut self, - kind: Dyn, + kind: TagDyn, walker: DynWalker<'a, 'ctx, E>, - ) -> Future<'a, 'ctx, DynFlow, E> { - self.tab(); - println!("Visit tag: {}", kind.0); - - E::wrap(async { - self.0 += 1; - let result = walker.walk(self).await; - self.0 -= 1; - // DynFlow::Flow(result) - DynFlow::Skipped + ) -> Future<'a, 'ctx, Status, E> { + E::wrap(async move { + match kind.0 { + crate::TAG_TYPE_NAME => { + self.tab(); + println!("type name:"); + + self.0 += 1; + walker.walk(self).await; + self.0 -= 1; + + Status::r#continue() + }, + crate::TAG_KEY => { + self.tab(); + println!("key:"); + + self.0 += 1; + walker.walk(self).await; + self.0 -= 1; + + Status::r#continue() + }, + crate::TAG_VALUE => { + self.tab(); + println!("value:"); + + self.0 += 1; + walker.walk(self).await; + self.0 -= 1; + + Status::r#continue() + }, + _ => Status::skipped() + } }) } } @@ -87,7 +117,7 @@ impl<'a, 'ctx: 'a, E: Effect<'ctx>> Value<'a, 'ctx, OwnedStatic<&'static str>, E Self: 'a, { self.tab(); - println!("Visit static str: {:?}", value); + println!("{:?}", value); E::ready(Flow::Continue) } } @@ -98,7 +128,7 @@ impl<'a, 'ctx: 'a, E: Effect<'ctx>> Value<'a, 'ctx, OwnedStatic<usize>, E> for V Self: 'a, { self.tab(); - println!("Visit usize: {}", value); + println!("{}", value); E::ready(Flow::Continue) } } @@ -109,7 +139,7 @@ impl<'a, 'ctx: 'a, E: Effect<'ctx>> Value<'a, 'ctx, OwnedStatic<bool>, E> for Vi Self: 'a, { self.tab(); - println!("Visit bool: {}", value); + println!("{}", value); E::ready(Flow::Continue) } } @@ -125,7 +155,7 @@ impl<'a, 'ctx: 'a, E: Effect<'ctx>> Value<'a, 'ctx, OwnedStatic<&'static [&'stat Self: 'a, { self.tab(); - println!("Visit static slice of static str: {:?}", value); + println!("{:?}", value); E::ready(Flow::Continue) } } @@ -146,20 +176,22 @@ impl<'ctx, E: Effect<'ctx>> Sequence<'ctx, E> for Visitor<E> { &'a mut self, scope: protocol::visitor::sequence::DynSequenceScope<'a, 'ctx, E>, ) -> Future<'a, 'ctx, Flow, E> { - self.tab(); E::wrap(async { - println!("Visit sequence, size hint: {:?}", scope.size_hint().await); + self.tab(); + println!("sequence{:?}", scope.size_hint().await); + self.0 += 1; let mut index = 0; let flow = loop { self.tab(); - println!("Calling next: {}", index); + println!("{}:", index); + self.0 += 1; match scope.next(self).await { Flow::Done => { self.tab(); - println!("Sequence done"); + println!("<end>"); break Flow::Continue; } Flow::Continue => {} @@ -28,7 +28,7 @@ use core::ops::ControlFlow; pub use build::*; use effect::{Effect, Future}; -use protocol::{Visitor, visitor::tag::TagError}; +use protocol::{visitor::tag::TagError, Visitor}; use symbol::Symbol; pub use transform::*; pub use walk::*; @@ -56,22 +56,18 @@ pub const TAG_TYPE_ID: Symbol = Symbol::new("Type ID"); pub const TAG_TYPE_SIZE: Symbol = Symbol::new("Type Size"); pub const TAG_FIELD_NAMES: Symbol = Symbol::new("Field Names"); pub const TAG_KEY: Symbol = Symbol::new("Key"); +pub const TAG_VALUE: Symbol = Symbol::new("Value"); pub const TAG_SEQ_INDEX: Symbol = Symbol::new("Seq Index"); pub const TAG_SEQ_LEN: Symbol = Symbol::new("Seq Length"); pub const TAG_STRUCT: Symbol = Symbol::new("Struct"); pub const TAG_MAP: Symbol = Symbol::new("Map"); pub const TAG_SEQ: Symbol = Symbol::new("Sequence"); pub const TAG_FIELD: Symbol = Symbol::new("Field"); +pub const TAG_KEY_VALUE: Symbol = Symbol::new("Key Value"); pub const TAG_ENUM: Symbol = Symbol::new("Enum"); pub enum DefaultMode {} -#[derive(Debug)] -pub enum StructWalkError { - Tag(TagError<never::Never>), - FieldTag(TagError<TagError<never::Never>>), -} - #[must_use] pub enum Flow { /// Processing should continue as normal. @@ -95,196 +91,86 @@ macro_rules! Walk { ),* $(,)?} } => { const _: () = { - impl<'ctx, M, E: $crate::effect::Effect<'ctx>> $crate::Walk<'ctx, M, E> for &'ctx $name { - type Walker = Walker<'ctx, M>; + impl<'ctx, M: 'ctx, E: $crate::effect::Effect<'ctx>> $crate::Walk<'ctx, M, E> for &'ctx $name { + type Walker = $crate::walkers::core::r#struct::StructWalker<'ctx, $name, Info, M, E>; fn into_walker(self) -> Self::Walker { - Walker { - value: self, - field: 0, - _marker: ::core::marker::PhantomData, - } + $crate::walkers::core::r#struct::StructWalker::new(self) } } impl<'ctx> $crate::WalkerTypes for &'ctx $name { - type Error = $crate::StructWalkError; + type Error = $crate::walkers::core::r#struct::StructWalkError<FieldError<'ctx>>; type Output = (); } - pub struct Walker<'ctx, M> { - value: &'ctx $name, - field: usize, - _marker: ::core::marker::PhantomData<fn() -> M>, - } + $vis enum Info {} - impl<'ctx, M> $crate::WalkerTypes for Walker<'ctx, M> { - type Error = $crate::StructWalkError; - type Output = (); - } + #[derive(Debug)] + #[allow(non_camel_case_types)] + enum FieldErrorKind<'ctx> {$( + $field($crate::walkers::core::key_value::KeyValueError<$crate::never::Never, <&'ctx $type as $crate::WalkerTypes>::Error>) + ),*} - impl<'ctx, E: $crate::effect::Effect<'ctx>, M> $crate::Walker<'ctx, E> for Walker<'ctx, M> { - fn walk<'a>( - mut self, - visitor: $crate::protocol::Visitor<'a, 'ctx>, - ) -> $crate::effect::Future<'a, 'ctx, Result<Self::Output, Self::Error>, E> - where - Self: 'a - { - E::wrap(async move { - // We should check if the visitor wants something specific. - if let Some(object) = visitor.upcast_mut::<$crate::protocol::visitor::request_hint::DynRequestHint<'_, 'ctx, E>>() { - // Allow the visitor to give a hint if it wants. - match object.request_hint(&mut self).await { - $crate::Flow::Continue => { - // The visitor wants the walker to continue to it's normal - // walking. - }, - _ => { - // The visitor is done (either because of an error or because - // it already used a hint). - return Ok(()); - }, - } - } - - // Attempt to visit the value directly. - if !matches!($crate::protocol::visitor::value::visit_value::<_, E>( - visitor, - $crate::any::static_wrapper::BorrowedStatic(self.value) - ).await, $crate::protocol::visitor::Status::Skipped) { - return Ok(()); - } - - // Follow the standard set of protocols for a struct. - // - Tagged: struct name - // - Tagged: struct type - // - Tagged: struct field names - // - Sequence: the fields - - // Describe the struct in a general way: - - use $crate::protocol::visitor::Status as S; - - // Give the type ID - match $crate::protocol::visitor::tag::visit_tag::<{ $crate::TAG_TYPE_ID.to_int() }, E, _>( - visitor, - $crate::walkers::core::value::ValueWalker::new(::core::any::TypeId::of::<$name>()) - ).await { - Ok(S::Skipped) | Ok(S::Continue) => {}, - Ok(S::Break) => return Ok(()), - Err(err) => return Err(StructWalkError::Tag(err)), - } - - // Signal this is a struct. - match $crate::protocol::visitor::tag::visit_tag::<{ $crate::TAG_STRUCT.to_int() }, E, _>( - visitor, - $crate::walkers::core::noop::NoopWalker::new() - ).await { - Ok(S::Skipped) => { - // If can't tag as a struct try as a map. - match $crate::protocol::visitor::tag::visit_tag::<{ $crate::TAG_MAP.to_int() }, E, _>( - visitor, - $crate::walkers::core::noop::NoopWalker::new() - ).await { - Ok(S::Skipped) | Ok(S::Continue) => {}, - Ok(S::Break) => return Ok(()), - Err(err) => return Err(StructWalkError::Tag(err)), - } - }, - Ok(S::Continue) => {}, - Ok(S::Break) => return Ok(()), - Err(err) => return Err(StructWalkError::Tag(err)), - } - - // Give the type name. - match $crate::protocol::visitor::tag::visit_tag::<{ $crate::TAG_TYPE_NAME.to_int() }, E, _>( - visitor, - $crate::walkers::core::value::ValueWalker::new(stringify!($name)) - ).await { - Ok(S::Skipped) | Ok(S::Continue) => {}, - Ok(S::Break) => return Ok(()), - Err(err) => return Err(StructWalkError::Tag(err)), - } - - // Give the field names before hand. - match $crate::protocol::visitor::tag::visit_tag::<{ $crate::TAG_FIELD_NAMES.to_int() }, E, _>( - visitor, - $crate::walkers::core::tag::StaticSliceWalker::<_, $crate::walkers::core::value::ValueWalker<&str>>::new(&[$( - stringify!($field) - ),*]) - ).await { - Ok(S::Skipped) | Ok(S::Continue) => {}, - Ok(S::Break) => return Ok(()), - Err(err) => return Err(StructWalkError::FieldTag(err)), - } - - if !matches!($crate::protocol::visitor::sequence::visit_sequence::<E>( - visitor, - &mut self, - ).await, $crate::protocol::visitor::Status::Skipped) { - return Ok(()); - } - - Ok(()) - }) - } - } + #[derive(Debug)] + $vis struct FieldError<'ctx>(FieldErrorKind<'ctx>); - impl<'ctx, E: $crate::effect::Effect<'ctx>, M> $crate::protocol::visitor::sequence::SequenceScope<'ctx, E> for Walker<'ctx, M> { - fn next<'a>(&'a mut self, visitor: Visitor<'a, 'ctx>) -> Future<'a, 'ctx, Flow, E> { - E::wrap(async { - use $crate::protocol::visitor::Status as S; - - let mut index = 0; - match self.field {$( - field if { index += 1; field == index - 1 } => { - match $crate::protocol::visitor::tag::visit_tag::<{ $crate::TAG_FIELD.to_int() }, E, _>( - visitor, - $crate::walkers::core::noop::NoopWalker::new() - ).await { - Ok(S::Skipped) | Ok(S::Continue) => {}, - Ok(S::Break) => return Flow::Break, - Err(err) => return Flow::Break, - } + impl<'ctx, M: 'ctx> $crate::walkers::core::r#struct::StructTypeInfo<'ctx, M> for Info { + const NAME: &'static str = stringify!($name); + const FIELDS: &'static [&'static str] = &[$(stringify!($field)),*]; - match $crate::protocol::visitor::tag::visit_tag::<{ $crate::TAG_KEY.to_int() }, E, _>( - visitor, - $crate::walkers::core::value::ValueWalker::new(stringify!($field)) - ).await { - Ok(S::Skipped) | Ok(S::Continue) => {}, - Ok(S::Break) => return Flow::Break, - Err(err) => return Flow::Break, - } + type FieldError = FieldError<'ctx>; + type T = $name; - // Use the type's walker in the same mode as the parent struct. - let walker = $crate::Walk::<M, E>::into_walker(&self.value.$field); - $crate::Walker::<E>::walk(walker, visitor).await; - self.field += 1; - Flow::Continue - } - )* - _ => Flow::Done - } - }) - } + #[allow(unreachable_code, non_snake_case, non_upper_case_globals, non_camel_case_types)] + fn walk_field<'a, E: $crate::effect::Effect<'ctx>>( + index: usize, + value: &'ctx Self::T, + visitor: $crate::Visitor<'a, 'ctx>, + ) -> $crate::effect::Future<'a, 'ctx, Result<$crate::Flow, Self::FieldError>, E> { + mod fields { + enum Fields {$($field),*} - fn size_hint<'a>(&'a mut self) -> Future<'a, 'ctx, (usize, Option<usize>), E> { - const X: &[&str] = &[$(stringify!($field)),*]; - E::ready((X.len(), Some(X.len()))) - } - } + $(pub const $field: usize = Fields::$field as usize;)* + } - $crate::any::any_trait! { - impl['a, 'ctx, M] Walker<'ctx, M> = [ - // dyn Hint<'a, 'ctx, OwnedStatic<bool>, E> + 'a, - ] - } + match index { + $(fields::$field => { + let key_walker = $crate::walkers::core::value::ValueWalker::new(stringify!($field)); + + let value_walker = <&'ctx $type as $crate::Walk::<'ctx, M, E>>::into_walker(&value.$field); + + let walker = $crate::walkers::core::key_value::KeyValueWalker::<$crate::protocol::visitor::tag::TagConst<{ $crate::TAG_FIELD.to_int() }>, _, _>::new($crate::protocol::visitor::tag::TagConst, key_walker, value_walker); + E::map($crate::Walker::<'ctx, E>::walk(walker, visitor), |result| match result { + Ok(_) => { + Ok(Flow::Continue) + } + Err(err) => { + Err(FieldError(FieldErrorKind::$field(err))) + } + }) + })* + _ => E::ready(Ok($crate::Flow::Done)) + } + } + } }; }; } +pub struct Demo { + pub a: bool, + pub b: bool, +} + +Walk! { + pub struct Demo { + a: bool, + b: bool, + } +} + #[cfg(test)] mod test { use crate::effect::{BlockOn, Blocking, Spin}; @@ -306,7 +192,11 @@ mod test { #[test] fn demo() { - let value = Demo { a: true, b: false, other: Other { value: true } }; + let value = Demo { + a: true, + b: false, + other: Other { value: true }, + }; let walker = Walk::<DefaultMode, Blocking>::into_walker(&value); let mut visitor = builders::debug::Visitor::<Blocking>::new(); diff --git a/src/macros.rs b/src/macros.rs index e69de29..8b13789 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -0,0 +1 @@ + diff --git a/src/protocol/visitor.rs b/src/protocol/visitor.rs index aad879d..9fc98c0 100644 --- a/src/protocol/visitor.rs +++ b/src/protocol/visitor.rs @@ -1,15 +1,41 @@ +use crate::Flow; + +pub mod recoverable; pub mod request_hint; pub mod sequence; pub mod tag; pub mod value; -pub enum Status { - /// The protocol was used. - Continue, - +pub enum Status<S = ()> { /// The protocol was not used. - Skipped, + Skipped(S), + + Flow(Flow) +} + +impl<S> Status<S> { + pub fn r#continue() -> Self { + Self::Flow(Flow::Continue) + } + + pub fn done() -> Self { + Self::Flow(Flow::Done) + } - /// Should stop processing. - Break, + pub fn r#break() -> Self { + Self::Flow(Flow::Break) + } } + +impl Status { + pub fn skipped() -> Self { + Self::Skipped(()) + } +} + +impl From<Flow> for Status { + fn from(value: Flow) -> Self { + Status::Flow(value) + } +} + diff --git a/src/protocol/visitor/recoverable.rs b/src/protocol/visitor/recoverable.rs new file mode 100644 index 0000000..f2d12f7 --- /dev/null +++ b/src/protocol/visitor/recoverable.rs @@ -0,0 +1,73 @@ +use crate::{ + effect::{Effect, Future}, + higher_ranked_type, + hkt::AnySend, + nameable, + protocol::{walker::hint::HintMeta, Visitor}, + Flow, +}; + +use super::Status; + +pub trait Recoverable<'ctx, E: Effect<'ctx>> { + fn visit<'a>( + &'a mut self, + scope: DynRecoverableScope<'a, 'ctx, E>, + ) -> Future<'a, 'ctx, Flow, E>; +} + +pub type DynRecoverable<'a, 'ctx, E> = &'a mut (dyn Recoverable<'ctx, E> + Send + 'a); + +nameable! { + pub struct Name['a, 'ctx, E]; + + impl [E] for DynRecoverable<'a, 'ctx, E> where { + E: Effect<'ctx>, + 'ctx: 'a + } + + impl [E] where DynRecoverable<'a, 'ctx, E> { + E: Effect<'ctx>, + 'ctx: 'a + } +} + +pub trait RecoverableScope<'ctx, E: Effect<'ctx>> { + fn new_walk<'a>(&'a mut self, visitor: Visitor<'a, 'ctx>) -> Future<'a, 'ctx, Flow, E>; +} + +pub type DynRecoverableScope<'a, 'ctx, E> = &'a mut (dyn RecoverableScope<'ctx, E> + Send + 'a); + +higher_ranked_type! { + pub type RecoverableKnownHkt['ctx]: (AnySend) = for<'lt> () +} + +impl<'a, 'ctx: 'a, E: Effect<'ctx>> HintMeta<'ctx> for DynRecoverable<'a, 'ctx, E> { + type Known = RecoverableKnownHkt<'ctx>; + + type Hint = (); +} + +pub fn visit_recoverable<'a, 'ctx, E: Effect<'ctx>>( + visitor: Visitor<'a, 'ctx>, + scope: DynRecoverableScope<'a, 'ctx, E>, +) -> Future<'a, 'ctx, Status, E> { + if let Some(object) = visitor.upcast_mut::<DynRecoverable<'_, 'ctx, E>>() { + // Allow the visitor to give a hint if it wants. + E::map(object.visit(scope), |flow| match flow { + Flow::Continue => { + // The visitor wants the walker to continue to it's normal + // walking. + Status::r#continue() + } + Flow::Break | Flow::Done => { + // The visitor is done (either because of an error or because + // it already used a hint). + Status::r#break() + } + }) + } else { + // If the visitor doesn't support request hint then we continue. + E::ready(Status::skipped()) + } +} diff --git a/src/protocol/visitor/request_hint.rs b/src/protocol/visitor/request_hint.rs index 50ae76d..f70fc29 100644 --- a/src/protocol/visitor/request_hint.rs +++ b/src/protocol/visitor/request_hint.rs @@ -1,7 +1,6 @@ use crate::{ effect::{Effect, Future}, nameable, - never::Never, protocol::{Visitor, Walker}, Flow, }; @@ -27,28 +26,20 @@ nameable! { /// Visit using the [`RequestHint`] protocol. /// -/// If `true` is returned then the walker should stop. -/// This is either because of an error or the visitor is done. +/// If [`Flow::Continue`] is returned then the visitor wants/needs more information it didn't get +/// from the hints. +/// If [`Flow::Done`] is returned then the visitor doesn't need any more information and the walker +/// should stop walking. +/// If [`Flow::Break`] is returned then there was an error and the walker should stop walking. pub fn visit_request_hint<'a, 'ctx, E: Effect<'ctx>>( visitor: Visitor<'a, 'ctx>, walker: Walker<'a, 'ctx>, -) -> Future<'a, 'ctx, bool, E> { +) -> Future<'a, 'ctx, Flow, E> { if let Some(object) = visitor.upcast_mut::<DynRequestHint<'_, 'ctx, E>>() { // Allow the visitor to give a hint if it wants. - E::map(object.request_hint(walker), |flow| match flow { - Flow::Continue => { - // The visitor wants the walker to continue to it's normal - // walking. - false - } - _ => { - // The visitor is done (either because of an error or because - // it already used a hint). - true - } - }) + object.request_hint(walker) } else { // If the visitor doesn't support request hint then we continue. - E::ready(false) + E::ready(Flow::Continue) } } diff --git a/src/protocol/visitor/sequence.rs b/src/protocol/visitor/sequence.rs index 8c7b2e4..d313e5b 100644 --- a/src/protocol/visitor/sequence.rs +++ b/src/protocol/visitor/sequence.rs @@ -38,22 +38,22 @@ pub trait SequenceScope<'ctx, E: Effect<'ctx>> { pub type DynSequenceScope<'a, 'ctx, E> = &'a mut (dyn SequenceScope<'ctx, E> + Send + 'a); higher_ranked_type! { - pub type KnownHkt['ctx]: (AnySend) = for<'lt> Known + pub type SequenceKnownHkt['ctx]: (AnySend) = for<'lt> SequenceKnown } #[derive(Default)] -pub struct Known { +pub struct SequenceKnown { pub len: (usize, Option<usize>), } -pub struct Hint { +pub struct SequenceHint { pub len: (usize, Option<usize>), } impl<'a, 'ctx: 'a, E: Effect<'ctx>> HintMeta<'ctx> for DynSequence<'a, 'ctx, E> { - type Known = KnownHkt<'ctx>; + type Known = SequenceKnownHkt<'ctx>; - type Hint = Hint; + type Hint = SequenceHint; } pub fn visit_sequence<'a, 'ctx, E: Effect<'ctx>>( @@ -66,16 +66,16 @@ pub fn visit_sequence<'a, 'ctx, E: Effect<'ctx>>( Flow::Continue => { // The visitor wants the walker to continue to it's normal // walking. - Status::Continue + Status::r#continue() } Flow::Break | Flow::Done => { // The visitor is done (either because of an error or because // it already used a hint). - Status::Break + Status::r#break() } }) } else { // If the visitor doesn't support request hint then we continue. - E::ready(Status::Skipped) + E::ready(Status::skipped()) } } diff --git a/src/protocol/visitor/tag.rs b/src/protocol/visitor/tag.rs index 2747c95..6a4fc1e 100644 --- a/src/protocol/visitor/tag.rs +++ b/src/protocol/visitor/tag.rs @@ -5,21 +5,24 @@ use crate::{ nameable, protocol::{walker::hint::HintMeta, Visitor}, symbol::Symbol, - DynWalker, Flow, WalkerTypes, DynWalkerAdapter, DynWalkerError, + DynWalker, DynWalkerAdapter, DynWalkerError, Flow, WalkerTypes, }; use super::Status; -pub trait Kind: 'static { - type Flow: Send; +pub trait TagKind: Copy + Send + 'static { + type Flow: Send + Into<Status>; fn symbol(&self) -> Symbol; } -pub struct Const<const SYMBOL: u64>; -pub struct Dyn(pub Symbol); +#[derive(Copy, Clone)] +pub struct TagConst<const SYMBOL: u64>; -impl<const SYMBOL: u64> Kind for Const<SYMBOL> { +#[derive(Copy, Clone)] +pub struct TagDyn(pub Symbol); + +impl<const SYMBOL: u64> TagKind for TagConst<SYMBOL> { type Flow = Flow; fn symbol(&self) -> Symbol { @@ -27,20 +30,15 @@ impl<const SYMBOL: u64> Kind for Const<SYMBOL> { } } -pub enum DynFlow { - Skipped, - Flow(Flow), -} - -impl Kind for Dyn { - type Flow = DynFlow; +impl TagKind for TagDyn { + type Flow = Status; fn symbol(&self) -> Symbol { self.0 } } -pub trait Tag<'ctx, K: Kind, E: Effect<'ctx>> { +pub trait Tag<'ctx, K: TagKind, E: Effect<'ctx>> { fn visit<'a>( &'a mut self, kind: K, @@ -54,34 +52,34 @@ nameable! { pub struct Name['a, 'ctx, K, E]; impl [K, E] for DynTag<'a, 'ctx, K, E> where { - K: Kind, + K: TagKind, E: Effect<'ctx>, 'ctx: 'a } impl [K, E] where DynTag<'a, 'ctx, K, E> { - K: Kind, + K: TagKind, E: Effect<'ctx>, 'ctx: 'a } } higher_ranked_type! { - pub type KnownHkt['ctx]: (AnySend) = for<'lt> Known + pub type TagKnownHkt['ctx]: (AnySend) = for<'lt> TagKnown } -pub struct Known { +pub struct TagKnown { pub kind_available: Option<bool>, } -pub struct Hint<K> { - pub kind: Option<K>, +pub struct TagHint<K> { + pub kind: K, } impl<'a, 'ctx: 'a, K, E> HintMeta<'ctx> for DynTag<'a, 'ctx, K, E> { - type Known = KnownHkt<'ctx>; + type Known = TagKnownHkt<'ctx>; - type Hint = Hint<K>; + type Hint = TagHint<K>; } #[derive(Debug)] @@ -89,10 +87,12 @@ pub enum TagErrorKind<E> { NeverWalked, /// This can only happen if a panic happens furing the walk and is then caught before calling - /// finish.. + /// finish. WalkNeverFinished, Walker(E), + + SkippedWasWalked, } #[derive(Debug)] @@ -102,92 +102,98 @@ pub struct TagError<E> { } impl<E> TagError<E> { - fn new<const SYMBOL: u64>(err: E) -> Self { + fn new<K: TagKind>(tag: K, err: E) -> Self { Self { - symbol: Symbol::from_int(SYMBOL), + symbol: tag.symbol(), err: TagErrorKind::Walker(err), } } - fn never_walked<const SYMBOL: u64>() -> Self { + fn never_walked<K: TagKind>(tag: K) -> Self { Self { - symbol: Symbol::from_int(SYMBOL), + symbol: tag.symbol(), err: TagErrorKind::NeverWalked, } } - fn walk_never_finished<const SYMBOL: u64>() -> Self { + fn walk_never_finished<K: TagKind>(tag: K) -> Self { Self { - symbol: Symbol::from_int(SYMBOL), + symbol: tag.symbol(), err: TagErrorKind::WalkNeverFinished, } } -} + fn was_walked<K: TagKind>(tag: K) -> Self { + Self { + symbol: tag.symbol(), + err: TagErrorKind::SkippedWasWalked, + } + } +} -pub fn visit_tag<'a, 'ctx: 'a, const SYMBOL: u64, E: Effect<'ctx>, W: crate::Walker<'ctx, E> + 'a>( +pub fn visit_tag<'a, 'ctx: 'a, K: TagKind, E: Effect<'ctx>, W: crate::Walker<'ctx, E> + 'a>( + kind: K, visitor: Visitor<'a, 'ctx>, walker: W, -) -> Future<'a, 'ctx, Result<Status, TagError<W::Error>>, E> +) -> Future<'a, 'ctx, Result<Status<W>, TagError<W::Error>>, E> where - W: WalkerTypes<Output = ()>, + W: WalkerTypes, { - E::wrap(async { - let result = - if let Some(object) = visitor.upcast_mut::<DynTag<'_, 'ctx, Const<SYMBOL>, E>>() { - // The visitor knows about this tag at compile time. + E::wrap(async move { + let (flow, walker) = + if let Some(object) = visitor.upcast_mut::<DynTag<'_, 'ctx, K, E>>() { + // The visitor knows about this tag. // Wrap the walker to allow it to be passed to a dyn walker argument. - let mut name_walker = DynWalkerAdapter::new(walker); + let mut walker = DynWalkerAdapter::new(walker); // Visit the tag. - let flow = object.visit(Const, &mut name_walker).await; - - // Finish the dynamic walker to get any errors from it. - let result = name_walker.finish(); - - (DynFlow::Flow(flow), result) - } else if let Some(object) = visitor.upcast_mut::<DynTag<'_, 'ctx, Dyn, E>>() { - // The visitor can handle dynamic tags. - // If the visitor can't handle the tag kind then it can call .skip on the walker - // to disable the error for not walking it. - - // Wrap the walker to allow it to be passed to a dyn walker argument. - let mut name_walker = DynWalkerAdapter::new(walker); - - // Visit the tag. - let flow = object - .visit(Dyn(Symbol::from_int(SYMBOL)), &mut name_walker) - .await; - - // Finish the dynamic walker to get any errors from it. - let result = name_walker.finish(); - - (flow, result) + let flow = object.visit(kind, &mut walker).await; + + (flow.into(), walker) + } else if core::any::TypeId::of::<TagDyn>() != core::any::TypeId::of::<K>() { + // Try the dynamic form if we didn't already. + if let Some(object) = visitor.upcast_mut::<DynTag<'_, 'ctx, TagDyn, E>>() { + // The visitor can handle dynamic tags. + // If the visitor can't handle the tag kind then it can call .skip on the walker + // to disable the error for not walking it. + + // Wrap the walker to allow it to be passed to a dyn walker argument. + let mut walker = DynWalkerAdapter::new(walker); + + // Visit the tag. + let flow = object + .visit(TagDyn(kind.symbol()), &mut walker) + .await; + + (flow, walker) + } else { + return Ok(Status::Skipped(walker)); + } } else { - return Ok(Status::Skipped); + return Ok(Status::Skipped(walker)); }; - match result { - // The happy path. - (DynFlow::Flow(Flow::Continue), Ok(_)) => Ok(Status::Continue), - - // The visitor wants to stop the control flow for some reason. - (DynFlow::Flow(Flow::Break), Ok(_)) => Ok(Status::Break), - (DynFlow::Flow(Flow::Done), Ok(_)) => Ok(Status::Break), - (DynFlow::Skipped, Ok(_)) => Ok(Status::Skipped), - - // The walker had an error. - (_, Err(DynWalkerError::Walker(err))) => Err(TagError::new::<SYMBOL>(err)), - - // The visitor never walked the dynamic walker. Aka it didn't call walk. - (_, Err(DynWalkerError::NeverWalked(_))) => Err(TagError::never_walked::<SYMBOL>()), - - // This is very hard to cause. The visitor must panic inside of .walk but then - // catch it in .visit. - (_, Err(DynWalkerError::WalkNeverFinished)) => { - Err(TagError::walk_never_finished::<SYMBOL>()) - } + match flow { + // The walker was skipped. + Status::Skipped(()) => { + match walker.into_innter() { + Ok(walker) => Ok(Status::Skipped(walker)), + Err(DynWalkerError::Walker(err)) => Err(TagError::new(kind, err)), + Err(DynWalkerError::NeverWalked(_)) => Err(TagError::never_walked(kind)), + Err(DynWalkerError::WalkNeverFinished) => Err(TagError::walk_never_finished(kind)), + Err(DynWalkerError::WasWalked(_)) => Err(TagError::was_walked(kind)), + } + }, + Status::Flow(flow) => { + match walker.finish() { + Ok(_) => Ok(Status::Flow(flow)), + Err(DynWalkerError::Walker(err)) => Err(TagError::new(kind, err)), + Err(DynWalkerError::NeverWalked(_)) => Err(TagError::never_walked(kind)), + Err(DynWalkerError::WalkNeverFinished) => Err(TagError::walk_never_finished(kind)), + Err(DynWalkerError::WasWalked(_)) => Err(TagError::was_walked(kind)), + } + }, } }) } diff --git a/src/protocol/visitor/value.rs b/src/protocol/visitor/value.rs index 8e7902c..3e9a3bc 100644 --- a/src/protocol/visitor/value.rs +++ b/src/protocol/visitor/value.rs @@ -8,7 +8,6 @@ use crate::{ higher_ranked_type, hkt::AnySend, nameable, - never::Never, protocol::{walker::hint::HintMeta, Visitor}, Flow, }; @@ -50,12 +49,12 @@ nameable! { } higher_ranked_type! { - pub type Known['ctx]: (AnySend) = for<'lt> () + pub type ValueKnownHkt['ctx]: (AnySend) = for<'lt> () } // This enrolls the Value protocol into the walker hint system. impl<'a, 'ctx: 'a, T, E: Effect<'ctx>> HintMeta<'ctx> for DynValue<'a, 'ctx, T, E> { - type Known = Known<'ctx>; + type Known = ValueKnownHkt<'ctx>; type Hint = (); } @@ -70,17 +69,17 @@ pub fn visit_value<'a, 'ctx, T: TypeNameable<'a, 'ctx>, E: Effect<'ctx>>( Flow::Continue => { // The visitor wants the walker to continue to it's normal // walking. - Status::Continue + Status::r#continue() } Flow::Break | Flow::Done => { // The visitor is done (either because of an error or because // it already used a hint). - Status::Break + Status::r#break() } }) } else { // If the visitor doesn't support request hint then we continue. - E::ready(Status::Skipped) + E::ready(Status::skipped()) } } diff --git a/src/protocol/walker/hint.rs b/src/protocol/walker/hint.rs index 52da09b..16b2515 100644 --- a/src/protocol/walker/hint.rs +++ b/src/protocol/walker/hint.rs @@ -12,6 +12,7 @@ use crate::{ hkt::AnySend, nameable, protocol::Visitor, + Flow, }; /// Meta information for the hint. @@ -31,35 +32,35 @@ pub trait HintMeta<'ctx> { pub type Known<'a, 'ctx, Protocol> = AnySend::T<'a, 'ctx, <Protocol as HintMeta<'ctx>>::Known>; /// Object implementing the [`Hint`] protocol. -pub trait Hint<'ctx, Protocol: ?Sized + HintMeta<'ctx>> { - type Effect: Effect<'ctx>; - +pub trait Hint<'a, 'ctx: 'a, Protocol: ?Sized + HintMeta<'ctx>, E: Effect<'ctx>> { /// Hint to the walker to use the `P` protocol. /// /// This should only be called once per [`RequestHint`]. - fn hint<'a>( + fn hint( &'a mut self, visitor: Visitor<'a, 'ctx>, hint: <Protocol as HintMeta<'ctx>>::Hint, - ) -> Future<'a, 'ctx, ControlFlow<(), ()>, Self::Effect>; + ) -> Future<'a, 'ctx, Flow, E>; /// Ask the walker for information about it's support of the protocol. - fn known<'a>( + fn known( &'a mut self, hint: &'a <Protocol as HintMeta<'ctx>>::Hint, - ) -> Future<'a, 'ctx, ControlFlow<(), Known<'a, 'ctx, Protocol>>, Self::Effect>; + ) -> Future<'a, 'ctx, Result<Known<'a, 'ctx, Protocol>, ()>, E>; } +pub type DynHint<'a, 'ctx, Protocol, E> = dyn Hint<'a, 'ctx, Protocol, E> + Send + 'a; + nameable! { pub struct Name['a, 'ctx, Protocol, E]; - impl [Protocol::Name, E] for dyn Hint<'ctx, Protocol, Effect = E> + 'a where { + impl [Protocol::Name, E] for DynHint<'a, 'ctx, Protocol, E> where { Protocol: TypeNameable<'a, 'ctx> + ?Sized, E: Effect<'ctx>, 'ctx: 'a, } - impl [Protocol, E] where dyn Hint<'ctx, Protocol::Nameable, Effect = E> + 'a { + impl [Protocol, E] where DynHint<'a, 'ctx, Protocol::Nameable, E> { Protocol: TypeName<'a, 'ctx> + ?Sized, E: Effect<'ctx>, 'ctx: 'a, @@ -90,22 +91,20 @@ mod test { impl for Y where {} } - impl<'ctx> Hint<'ctx, Y> for X<'ctx> { - type Effect = Blocking; - - fn hint<'a>( + impl<'a, 'ctx: 'a, E: Effect<'ctx>> Hint<'a, 'ctx, Y, E> for X<'ctx> { + fn hint( &'a mut self, _visitor: Visitor<'a, 'ctx>, _hint: <Y as HintMeta<'ctx>>::Hint, - ) -> Future<'a, 'ctx, ControlFlow<(), ()>, Self::Effect> { + ) -> Future<'a, 'ctx, Flow, E> { todo!() } - fn known<'a>( + fn known( &'a mut self, _hint: &'a <Y as HintMeta<'ctx>>::Hint, - ) -> Future<'a, 'ctx, ControlFlow<(), Known<'a, 'ctx, Y>>, Self::Effect> { - Self::Effect::wrap(async { ControlFlow::Continue(&mut *self.0) }) + ) -> Future<'a, 'ctx, Result<Known<'a, 'ctx, Y>, ()>, E> { + E::ready(Ok(&mut *self.0)) } } @@ -121,15 +120,15 @@ mod test { let mut z = 42; let mut x = X(&mut z); - let y: &mut dyn Hint<'_, Y, Effect = Blocking> = &mut x; + let y: &mut DynHint<'_, '_, Y, Blocking> = &mut x; fn id<'a, 'ctx, T: ?Sized + TypeNameable<'a, 'ctx>>(_x: &T) {} id(y); let x = Spin::block_on(y.known(&())); match x { - ControlFlow::Continue(value) => *value += 1, - ControlFlow::Break(_) => todo!(), + Ok(value) => *value += 1, + Err(_) => todo!(), } assert_eq!(z, 43); } diff --git a/src/walk.rs b/src/walk.rs index 0dbccee..c99251d 100644 --- a/src/walk.rs +++ b/src/walk.rs @@ -11,6 +11,7 @@ pub trait Walk<'ctx, M, E: Effect<'ctx>>: WalkerTypes + Sized { /// The walker for the type. type Walker: Walker<'ctx, E, Error = Self::Error, Output = Self::Output>; + #[must_use] fn into_walker(self) -> Self::Walker; } @@ -48,14 +49,11 @@ pub trait WalkerObjSafe<'ctx, E: Effect<'ctx>>: Send { fn walk<'a>(&'a mut self, visitor: Visitor<'a, 'ctx>) -> Future<'a, 'ctx, Flow, E> where Self: 'a; - - fn skip(&mut self); } pub type DynWalker<'a, 'ctx, E> = &'a mut (dyn WalkerObjSafe<'ctx, E> + Send + 'a); enum DynWalkerState<W: WalkerTypes> { - Skipped, Walking, Pending(W), Done(W::Output), @@ -70,6 +68,8 @@ pub enum DynWalkerError<W: WalkerTypes> { WalkNeverFinished, Walker(W::Error), + + WasWalked(W::Output), } pub struct DynWalkerAdapter<W: WalkerTypes> { @@ -83,12 +83,20 @@ impl<W: WalkerTypes> DynWalkerAdapter<W> { } } - pub fn finish(self) -> Result<Option<W::Output>, DynWalkerError<W>> { + pub fn finish(self) -> Result<W::Output, DynWalkerError<W>> { match self.state { - DynWalkerState::Skipped => Ok(None), DynWalkerState::Walking => Err(DynWalkerError::WalkNeverFinished), DynWalkerState::Pending(walker) => Err(DynWalkerError::NeverWalked(walker)), - DynWalkerState::Done(value) => Ok(Some(value)), + DynWalkerState::Done(value) => Ok(value), + DynWalkerState::Err(err) => Err(DynWalkerError::Walker(err)), + } + } + + pub fn into_innter(self) -> Result<W, DynWalkerError<W>> { + match self.state { + DynWalkerState::Walking => Err(DynWalkerError::WalkNeverFinished), + DynWalkerState::Pending(walker) => Ok(walker), + DynWalkerState::Done(value) => Err(DynWalkerError::WasWalked(value)), DynWalkerState::Err(err) => Err(DynWalkerError::Walker(err)), } } @@ -123,8 +131,4 @@ impl<'ctx, W: Walker<'ctx, E>, E: Effect<'ctx>> WalkerObjSafe<'ctx, E> for DynWa } }) } - - fn skip(&mut self) { - self.state = DynWalkerState::Skipped; - } } diff --git a/src/walk/walkers/core.rs b/src/walk/walkers/core.rs index 172244d..b1cc7af 100644 --- a/src/walk/walkers/core.rs +++ b/src/walk/walkers/core.rs @@ -2,5 +2,7 @@ pub mod bool; pub mod noop; +pub mod r#struct; pub mod tag; pub mod value; +pub mod key_value; diff --git a/src/walk/walkers/core/bool.rs b/src/walk/walkers/core/bool.rs index a3f2a93..5e173fa 100644 --- a/src/walk/walkers/core/bool.rs +++ b/src/walk/walkers/core/bool.rs @@ -1,6 +1,6 @@ use crate::{effect::Effect, Walk, WalkerTypes}; -use super::value::{ValueWalker, BorrowWalker}; +use super::value::{BorrowWalker, ValueWalker}; impl<'ctx, M, E: Effect<'ctx>> Walk<'ctx, M, E> for bool { type Walker = ValueWalker<bool>; diff --git a/src/walk/walkers/core/key_value.rs b/src/walk/walkers/core/key_value.rs new file mode 100644 index 0000000..3ac5b6b --- /dev/null +++ b/src/walk/walkers/core/key_value.rs @@ -0,0 +1,92 @@ +use crate::{never::Never, WalkerTypes, effect::{Effect, Future}, protocol::{Visitor, visitor::{tag::{visit_tag, TagKind, TagConst, TagError}, Status}}, walkers::core::noop::NoopWalker, TAG_KEY_VALUE, Flow, TAG_KEY, TAG_VALUE}; + +pub struct KeyValueWalker<T, K, V> { + key_walker: K, + value_walker: V, + tag: T, +} + +impl<T, K, V> KeyValueWalker<T, K, V> { + pub fn new(tag: T, key_walker: K, value_walker: V) -> Self { + Self { + key_walker, + value_walker, + tag, + } + } +} + +#[derive(Debug)] +enum KeyValueErrorKind<K, V> { + Tag(TagError<Never>), + Key(K), + Value(V), +} + +#[derive(Debug)] +pub struct KeyValueError<K, V>(KeyValueErrorKind<K, V>); + +impl<T, K, V> WalkerTypes for KeyValueWalker<T, K, V> +where + K: WalkerTypes, + V: WalkerTypes, +{ + type Error = KeyValueError<K::Error, V::Error>; + + type Output = (); +} + +impl<'ctx, T, K, V, E> crate::Walker<'ctx, E> for KeyValueWalker<T, K, V> +where + E: Effect<'ctx>, + T: TagKind, + K: crate::Walker<'ctx, E>, + V: crate::Walker<'ctx, E>, +{ + fn walk<'a>( + self, + visitor: Visitor<'a, 'ctx>, + ) -> Future<'a, 'ctx, Result<Self::Output, Self::Error>, E> + where + Self: 'a + { + E::wrap(async move { + match visit_tag::<T, E, _>(self.tag, visitor, NoopWalker::new()).await { + Ok(Status::Skipped(_)) => { + match visit_tag::<TagConst<{ TAG_KEY_VALUE.to_int() }>, E, _>(TagConst, visitor, NoopWalker::new()).await + { + Ok(Status::Skipped(_) | Status::Flow(Flow::Continue)) => {}, + Ok(Status::Flow(flow)) => return Ok(()), + Err(_) => todo!(), + } + } + Ok(Status::Flow(Flow::Continue)) => {}, + Ok(Status::Flow(flow)) => todo!(), + Err(_) => todo!(), + } + + match visit_tag::<TagConst<{ TAG_KEY.to_int() }>, E, _>(TagConst, visitor, self.key_walker).await + { + Ok(Status::Skipped(_) | Status::Flow(Flow::Continue)) => {}, + Ok(Status::Flow(flow)) => return Ok(()), + Err(_) => todo!(), + } + + match visit_tag::<TagConst<{ TAG_VALUE.to_int() }>, E, _>(TagConst, visitor, self.value_walker).await + { + Ok(Status::Flow(Flow::Continue)) => {}, + Ok(Status::Skipped(value_walker)) => { + // Fallback to just walking the value. + match value_walker.walk(visitor).await { + Ok(_) => {}, + Err(err) => todo!(), + } + }, + Ok(Status::Flow(flow)) => todo!(), + Err(_) => todo!(), + } + + Ok(()) + }) + } +} 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 + }) + } +} diff --git a/src/walk/walkers/core/tag.rs b/src/walk/walkers/core/tag.rs index 3bbe522..8ad92ec 100644 --- a/src/walk/walkers/core/tag.rs +++ b/src/walk/walkers/core/tag.rs @@ -10,8 +10,8 @@ use crate::{ request_hint::{visit_request_hint, DynRequestHint, RequestHint}, sequence::{DynSequence, SequenceScope}, tag::{DynTag, TagError}, - Status, value::{visit_value, DynValue, Value}, + Status, }, Visitor, }, @@ -57,15 +57,16 @@ where Self: 'a, { E::wrap(async move { - if visit_request_hint::<E>(visitor, &mut self).await { - return Ok(()); + match visit_request_hint::<E>(visitor, &mut self).await { + Flow::Continue => {}, + _ => return Ok(()), } - if matches!( - visit_value::<_, E>(visitor, OwnedStatic(self.names)).await, - Status::Break | Status::Continue - ) { - return Ok(()); + match visit_value::<_, E>(visitor, OwnedStatic(self.names)).await + { + Status::Skipped(_) => {}, + Status::Flow(Flow::Continue) => {}, + _ => return Ok(()), } if let Some(object) = visitor.upcast_mut::<DynSequence<'_, 'ctx, E>>() { |