use effectful::{
effective::Effective,
environment::{DynBind, Environment, NativeForm},
for_lt,
higher_ranked::Mut,
};
use crate::{
any::TypeName,
hkt::Marker,
protocol::{DynVisitor, DynWalker},
};
use super::VisitResult;
/// Protocol for requesting a hint from a visitor.
pub trait RequestHint<'ctx, E: Environment>: DynBind<E> {
/// 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<'this: 'e, 'walker: 'e, 'e>(
&'this mut self,
walker: DynWalker<'walker, 'ctx, E>,
) -> NativeForm<'e, VisitResult, E>
where
'ctx: 'this + 'walker;
}
pub struct RequestHintProto<E: Environment>(Marker<E>);
impl<'a, 'ctx, E> TypeName::MemberTypeForLt<'a, 'ctx, E, &'a &'ctx ()> for RequestHintProto<E>
where
E: Environment,
{
type T = dyn RequestHint<'ctx, E> + 'a;
}
impl<'a, 'ctx, E> TypeName::LowerTypeWithBound<'a, 'ctx, E, &'a &'ctx ()>
for dyn RequestHint<'ctx, E> + 'a
where
E: Environment,
{
type Higher = RequestHintProto<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<'ctx: 'visitor + 'walker, 'visitor: 'e, 'walker: 'e, 'e, E: Environment>(
visitor: DynVisitor<'visitor, 'ctx, E>,
walker: DynWalker<'walker, 'ctx, E>,
) -> NativeForm<'e, VisitResult<DynWalker<'walker, 'ctx, E>>, E> {
E::value(walker)
.update(visitor, |visitor, walker| {
if let Some(object) = visitor.0.upcast_mut::<RequestHintProto<E>>() {
// Allow the visitor to give a hint if it wants.
object
.request_hint(walker.cast())
.map((), |_, x| x.unit_skipped())
.cast::<()>();
todo!()
} 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()
}