documented struct flow
| -rw-r--r-- | protocols.md | 52 | ||||
| -rw-r--r-- | src/build/builders/debug.rs | 7 | ||||
| -rw-r--r-- | src/lib.rs | 81 | ||||
| -rw-r--r-- | src/macros.rs | 120 | ||||
| -rw-r--r-- | src/protocol/visitor/tag.rs | 131 | ||||
| -rw-r--r-- | src/walk/walkers/core/tag.rs | 3 |
6 files changed, 224 insertions, 170 deletions
diff --git a/protocols.md b/protocols.md index f79e1c9..8d80900 100644 --- a/protocols.md +++ b/protocols.md @@ -1,7 +1,45 @@ -struct -- name -- type_id -- fields - - name - - index - - type_id +# Walker + +## General Layout +All the standard flows use the following structure. + +- Request Hint: Allow the builder to get exactly what it wants instead of using the full flow. +- Header Tags: Tags for meta information about the value. These are what provide the rich structure of data. +- Sequence / Value: The two basic ways to pass data are by a sequence of walkers or a single value. + +Nothing stops a walker from emitting multiple values to form a sequence. However, most builders +will not expect this behavior. Instead, the sequence protocol allows a builder to construct a sequence +of values *it* wants instead of what the walker is generating. + +## Map +A sequence of key tagged values + +A map emits everything a plain sequence would. +This allows using maps with builders that only undertand sequences. +Note, the keys of the values will be lost if the builder doesn't understand key tags. + +- Tag - Map: A empty value that signals the value is a map instead of a normal sequence. +- Sequence: A sequence of the key tagged values. + - Tag - Key: A (hopefully unique) key for the value. + - <flatten>: The value. + +## Struct +A struct in Rust and more generally as a map. + +A struct emits everything a map would, and also it emits everything a plain sequence would. +This allows using structs with builders that only understand maps and/or sequences. + +- Request Hint: Allow the builder to directly ask for what it wants. +- Value: Attempt to give the struct directly to the builder. +- Tag - Type ID: Useful for looking up things in a type map. Only visited if the struct is a Rust type. +- Tag - Struct: A empty value that signals the value is a struct instead of just a map. + - Tag - Map: This only happens if the visitor didn't recognize the struct tag. +- Tag - Type Name: The name of the type. +- Tag - Field Names: All the field names of the struct. + - Sequence: A sequence of strings. + - Value: Field name as a `&'static str`. +- Sequence: A sequence of the fields. + - Tag - Field: A empty value that signals the value is a struct field instead of just a map key. + - Tag - Key: The field name. + - Value: Field name as a `&'static str`. + - <flatten>: The value. diff --git a/src/build/builders/debug.rs b/src/build/builders/debug.rs index c8f9324..4a51281 100644 --- a/src/build/builders/debug.rs +++ b/src/build/builders/debug.rs @@ -10,7 +10,7 @@ use crate::{ visitor::{ request_hint::{DynRequestHint, RequestHint}, sequence::{DynSequence, Sequence}, - tag::{Dyn, DynTag, Tag}, + tag::{Dyn, DynTag, Tag, DynFlow}, value::{DynValue, Value}, }, }, @@ -64,7 +64,7 @@ impl<'ctx, E: Effect<'ctx>> Tag<'ctx, Dyn, E> for Visitor<E> { &'a mut self, kind: Dyn, walker: DynWalker<'a, 'ctx, E>, - ) -> Future<'a, 'ctx, Flow, E> { + ) -> Future<'a, 'ctx, DynFlow, E> { self.tab(); println!("Visit tag: {}", kind.0); @@ -72,7 +72,8 @@ impl<'ctx, E: Effect<'ctx>> Tag<'ctx, Dyn, E> for Visitor<E> { self.0 += 1; let result = walker.walk(self).await; self.0 -= 1; - result + // DynFlow::Flow(result) + DynFlow::Skipped }) } } @@ -28,8 +28,7 @@ use core::ops::ControlFlow; pub use build::*; use effect::{Effect, Future}; -use macros::TagError; -use protocol::Visitor; +use protocol::{Visitor, visitor::tag::TagError}; use symbol::Symbol; pub use transform::*; pub use walk::*; @@ -60,6 +59,8 @@ pub const TAG_KEY: Symbol = Symbol::new("Key"); 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_ENUM: Symbol = Symbol::new("Enum"); @@ -68,6 +69,7 @@ pub enum DefaultMode {} #[derive(Debug)] pub enum StructWalkError { Tag(TagError<never::Never>), + FieldTag(TagError<TagError<never::Never>>), } #[must_use] @@ -146,12 +148,6 @@ macro_rules! Walk { } } - // Follow the standard set of protocols for a struct. - // - Tagged: struct name - // - Tagged: struct type - // - Tagged: struct field names - // - Sequence: the fields - // Attempt to visit the value directly. if !matches!($crate::protocol::visitor::value::visit_value::<_, E>( visitor, @@ -160,48 +156,67 @@ macro_rules! Walk { 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::macros::visit_tag::<{ $crate::TAG_TYPE_ID.to_int() }, E, _>( + 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($crate::Flow::Continue) => {}, - Ok(_) => return Ok(()), - Err(_) => unreachable!(), + 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::macros::visit_tag::<{ $crate::TAG_STRUCT.to_int() }, E, _>( + match $crate::protocol::visitor::tag::visit_tag::<{ $crate::TAG_STRUCT.to_int() }, E, _>( visitor, $crate::walkers::core::noop::NoopWalker::new() ).await { - Ok($crate::Flow::Continue) => {}, - _ => return Ok(()), + 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::macros::visit_tag::<{ $crate::TAG_TYPE_NAME.to_int() }, E, _>( + 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($crate::Flow::Continue) => {}, - Ok(_) => return Ok(()), - Err(_) => unreachable!(), + 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::macros::visit_tag::<{ $crate::TAG_FIELD_NAMES.to_int() }, E, _>( + 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($crate::Flow::Continue) => {}, - Ok(_) => return Ok(()), - // Err(err) => return Err(StructWalkError::Tag(err)), - Err(err) => todo!(), + 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>( @@ -219,26 +234,30 @@ macro_rules! Walk { 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::macros::visit_tag::<{ $crate::TAG_FIELD.to_int() }, E, _>( + match $crate::protocol::visitor::tag::visit_tag::<{ $crate::TAG_FIELD.to_int() }, E, _>( visitor, $crate::walkers::core::noop::NoopWalker::new() ).await { - Ok($crate::Flow::Continue) => {}, - _ => return Flow::Done, + Ok(S::Skipped) | Ok(S::Continue) => {}, + Ok(S::Break) => return Flow::Break, + Err(err) => return Flow::Break, } - match $crate::macros::visit_tag::<{ $crate::TAG_KEY.to_int() }, E, _>( + match $crate::protocol::visitor::tag::visit_tag::<{ $crate::TAG_KEY.to_int() }, E, _>( visitor, $crate::walkers::core::value::ValueWalker::new(stringify!($field)) ).await { - Ok($crate::Flow::Continue) => {}, - Ok(_) => return Flow::Done, - Err(_) => unreachable!(), + Ok(S::Skipped) | Ok(S::Continue) => {}, + Ok(S::Break) => return Flow::Break, + Err(err) => return Flow::Break, } + // 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; diff --git a/src/macros.rs b/src/macros.rs index ffb52b1..e69de29 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,120 +0,0 @@ -use crate::{ - effect::{Effect, Future}, - protocol::{ - visitor::tag::{self, DynTag}, - Visitor, - }, - symbol::Symbol, - DynWalkerAdapter, DynWalkerError, Flow, Walker, WalkerTypes, -}; - -#[derive(Debug)] -pub enum TagErrorKind<E> { - NeverWalked, - - /// This can only happen if a panic happens furing the walk and is then caught before calling - /// finish.. - WalkNeverFinished, - - Walker(E), -} - -#[derive(Debug)] -pub struct TagError<E> { - symbol: Symbol, - err: TagErrorKind<E>, -} - -impl<E> TagError<E> { - fn new<const SYMBOL: u64>(err: E) -> Self { - Self { - symbol: Symbol::from_int(SYMBOL), - err: TagErrorKind::Walker(err), - } - } - - fn never_walked<const SYMBOL: u64>() -> Self { - Self { - symbol: Symbol::from_int(SYMBOL), - err: TagErrorKind::NeverWalked, - } - } - - fn walk_never_finished<const SYMBOL: u64>() -> Self { - Self { - symbol: Symbol::from_int(SYMBOL), - err: TagErrorKind::WalkNeverFinished, - } - } -} - -pub fn visit_tag<'a, 'ctx: 'a, const SYMBOL: u64, E: Effect<'ctx>, W: Walker<'ctx, E> + 'a>( - visitor: Visitor<'a, 'ctx>, - walker: W, -) -> Future<'a, 'ctx, Result<Flow, TagError<W::Error>>, E> -where - W: WalkerTypes<Output = ()>, -{ - E::wrap(async { - let result = - if let Some(object) = visitor.upcast_mut::<DynTag<'_, 'ctx, tag::Const<SYMBOL>, E>>() { - // The visitor knows about this tag at compile time. - - // 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(tag::Const, &mut name_walker).await; - - // Finish the dynamic walker to get any errors from it. - let result = name_walker.finish(); - - Some((flow, result)) - } else if let Some(object) = visitor.upcast_mut::<DynTag<'_, 'ctx, tag::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(tag::Dyn(Symbol::from_int(SYMBOL)), &mut name_walker) - .await; - - // Finish the dynamic walker to get any errors from it. - let result = name_walker.finish(); - - Some((flow, result)) - } else { - None - }; - - let Some(result) = result else { - // The visitor doesn't know about this tag protocol so just continue as normal. - return Ok(Flow::Continue); - }; - - match result { - // The happy path. - (Flow::Continue, Ok(_)) => Ok(Flow::Continue), - - // The visitor wants to stop the control flow for some reason. - (Flow::Break, Ok(_)) => Ok(Flow::Break), - (Flow::Done, Ok(_)) => Ok(Flow::Done), - - // 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>()) - } - } - }) -} diff --git a/src/protocol/visitor/tag.rs b/src/protocol/visitor/tag.rs index 1be53ae..2747c95 100644 --- a/src/protocol/visitor/tag.rs +++ b/src/protocol/visitor/tag.rs @@ -1,18 +1,18 @@ -use core::ops::ControlFlow; - use crate::{ - any::{TypeName, TypeNameable}, effect::{Effect, Future}, higher_ranked_type, hkt::AnySend, nameable, - never::Never, - protocol::{walker::hint::HintMeta, Visitor, Walker}, + protocol::{walker::hint::HintMeta, Visitor}, symbol::Symbol, - DynWalker, Flow, + DynWalker, Flow, WalkerTypes, DynWalkerAdapter, DynWalkerError, }; +use super::Status; + pub trait Kind: 'static { + type Flow: Send; + fn symbol(&self) -> Symbol; } @@ -20,12 +20,21 @@ pub struct Const<const SYMBOL: u64>; pub struct Dyn(pub Symbol); impl<const SYMBOL: u64> Kind for Const<SYMBOL> { + type Flow = Flow; + fn symbol(&self) -> Symbol { Symbol::from_int(SYMBOL) } } +pub enum DynFlow { + Skipped, + Flow(Flow), +} + impl Kind for Dyn { + type Flow = DynFlow; + fn symbol(&self) -> Symbol { self.0 } @@ -36,7 +45,7 @@ pub trait Tag<'ctx, K: Kind, E: Effect<'ctx>> { &'a mut self, kind: K, walker: DynWalker<'a, 'ctx, E>, - ) -> Future<'a, 'ctx, Flow, E>; + ) -> Future<'a, 'ctx, K::Flow, E>; } pub type DynTag<'a, 'ctx, K, E> = dyn Tag<'ctx, K, E> + Send + 'a; @@ -74,3 +83,111 @@ impl<'a, 'ctx: 'a, K, E> HintMeta<'ctx> for DynTag<'a, 'ctx, K, E> { type Hint = Hint<K>; } + +#[derive(Debug)] +pub enum TagErrorKind<E> { + NeverWalked, + + /// This can only happen if a panic happens furing the walk and is then caught before calling + /// finish.. + WalkNeverFinished, + + Walker(E), +} + +#[derive(Debug)] +pub struct TagError<E> { + symbol: Symbol, + err: TagErrorKind<E>, +} + +impl<E> TagError<E> { + fn new<const SYMBOL: u64>(err: E) -> Self { + Self { + symbol: Symbol::from_int(SYMBOL), + err: TagErrorKind::Walker(err), + } + } + + fn never_walked<const SYMBOL: u64>() -> Self { + Self { + symbol: Symbol::from_int(SYMBOL), + err: TagErrorKind::NeverWalked, + } + } + + fn walk_never_finished<const SYMBOL: u64>() -> Self { + Self { + symbol: Symbol::from_int(SYMBOL), + err: TagErrorKind::WalkNeverFinished, + } + } +} + + +pub fn visit_tag<'a, 'ctx: 'a, const SYMBOL: u64, E: Effect<'ctx>, W: crate::Walker<'ctx, E> + 'a>( + visitor: Visitor<'a, 'ctx>, + walker: W, +) -> Future<'a, 'ctx, Result<Status, TagError<W::Error>>, E> +where + W: WalkerTypes<Output = ()>, +{ + 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. + + // 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(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) + } else { + return Ok(Status::Skipped); + }; + + 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>()) + } + } + }) +} diff --git a/src/walk/walkers/core/tag.rs b/src/walk/walkers/core/tag.rs index 5c5d144..3bbe522 100644 --- a/src/walk/walkers/core/tag.rs +++ b/src/walk/walkers/core/tag.rs @@ -4,13 +4,12 @@ use crate::{ any::static_wrapper::OwnedStatic, any_trait, effect::{Effect, Future}, - macros::{visit_tag, TagError}, never::Never, protocol::{ visitor::{ request_hint::{visit_request_hint, DynRequestHint, RequestHint}, sequence::{DynSequence, SequenceScope}, - tag::DynTag, + tag::{DynTag, TagError}, Status, value::{visit_value, DynValue, Value}, }, |