use effectful::{ effective::{Canonical, Effective}, environment::Environment, DynBind, }; use crate::{ any::type_name, protocol::{DynVisitor, DynWalker}, }; use super::VisitResult; /// Protocol for requesting a hint from a visitor. pub trait RequestHint<'src, E: Environment>: DynBind { /// Call this to request a hint. /// /// `walker` is what the visitor (`self`) will call to give a hint using the /// [`Hint`][crate::builtins::walker::Hint] protocol. fn request_hint<'r>( &'r mut self, walker: DynWalker<'r, 'src, E>, ) -> Canonical<'r, VisitResult, E>; } impl<'u, 'src, E> type_name::Lower<'u, 'src, &'u &'src ()> for dyn RequestHint<'static, E> where E: Environment, { type Lowered = dyn RequestHint<'src, E> + 'u; } impl<'u, 'src, E> type_name::Raise<'u, 'src, &'u &'src ()> for dyn RequestHint<'src, E> + 'u where E: Environment, { type Raised = dyn RequestHint<'static, E>; } /// Visit using the [`RequestHint`] protocol. /// /// 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 request_hint<'r, 'src, E: Environment>( visitor: DynVisitor<'r, 'src, E>, walker: DynWalker<'r, 'src, E>, ) -> Canonical<'r, VisitResult>, E> { E::value((visitor, walker)) .update_map((), |_, (visitor, walker)| { if let Some(object) = visitor.upcast_mut:: + '_>() { // Allow the visitor to give a hint if it wants. object .request_hint(walker.cast()) .map((), |_, x| x.unit_skipped()) .cast() } else { // If the visitor doesn't support request hint then we continue. E::value(VisitResult::Skipped(())).cast() } }) .map((), |_, ((_, walker), result)| { result.map_skipped(|_| walker) }) .cast() }