Diffstat (limited to 'src/macros.rs')
| -rw-r--r-- | src/macros.rs | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..a946257 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,120 @@ +use crate::{ + effect::{Effect, Future}, + protocol::{ + visitor::tag::{self, DynTag}, + Visitor, + }, + symbol::Symbol, + DynWalkerAdapter, DynWalkerError, Flow, Walker, WalkerTypes, +}; + +#[derive(Debug)] +pub struct TagError { + symbol: Symbol, + msg: &'static str, +} + +impl From<TagError> for &'static str { + fn from(value: TagError) -> Self { + value.msg + } +} + +impl TagError { + fn new<const SYMBOL: u64>(msg: &'static str) -> Self { + Self { + symbol: Symbol::from_int(SYMBOL), + msg, + } + } + + fn never_walked<const SYMBOL: u64>() -> Self { + Self::new::<SYMBOL>("visitor did not walk the walker") + } + + fn walk_never_finished<const SYMBOL: u64>() -> Self { + Self::new::<SYMBOL>("walk did not finish") + } +} + +pub fn visit_tag< + 'a, + 'ctx: 'a, + const SYMBOL: u64, + E: Effect<'ctx>, + W: Walker<'ctx, Effect = E> + 'a, +>( + visitor: Visitor<'a, 'ctx>, + walker: W, +) -> Future<'a, 'ctx, Flow<TagError>, E> +where + W: WalkerTypes<Output = ()>, + <W as WalkerTypes>::Error: Into<&'static str>, +{ + 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 Flow::Continue; + }; + + match result { + // The happy path. + (Flow::Continue, Ok(_)) => Flow::Continue, + + // The visitor wants to stop the control flow for some reason. + (Flow::Break, Ok(_)) => Flow::Break, + + // The walker had an error. + (_, Err(DynWalkerError::Walker(err))) => Flow::Err(TagError::new::<SYMBOL>(err.into())), + + // The visitor never walked the dynamic walker. Aka it didn't call walk. + (_, Err(DynWalkerError::NeverWalked(_))) => { + Flow::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)) => { + Flow::Err(TagError::walk_never_finished::<SYMBOL>()) + } + + // This isn't possible because Err(!). + (Flow::Err(_), _) => unreachable!(), + } + }) +} |