//! Protocol for giving a hint to a walker.
//!
//! Sometimes a walker has multiple protocols it could use,
//! this module gives a protocol by which a visitor can give a hint
//! to the walker about what it is expecting.
use core::ops::{Deref, DerefMut};
use effectful::{
effective::Effective,
environment::{DynBind, EnvConfig, Environment, InEnvironment, NativeForm},
higher_ranked::{Hrt, WithLt},
SendSync,
};
use crate::{
any::{type_name, AnyTrait},
hkt::Marker,
protocol::{visitor::VisitResult, AnyTraitDynBind, DynVisitor, DynWalker},
};
/// Meta information for the hint.
///
/// This gives the visitor more information to work from when selecting a hint.
pub trait HintMeta: InEnvironment + type_name::Static {
/// Information known by the walker.
///
/// This should be information easy to get without changing the state of the walker
/// in an irreversible way.
type Known: Hrt;
/// Extra information the visitor can give to the walker about what it is expecting.
type Hint: Hrt;
}
/// Object implementing the [`Hint`] protocol.
pub trait Hint<'ctx, Protocol: ?Sized + HintMeta>: DynBind<Protocol::Env> {
/// Hint to the walker to use the `P` protocol.
///
/// This should only be called once per [`RequestHint`].
fn hint<'this: 'e, 'visitor: 'e, 'hint: 'e, 'e>(
&'this mut self,
visitor: DynVisitorWith<'visitor, 'ctx, Protocol>,
hint: WithLt<'hint, Protocol::Hint>,
) -> NativeForm<'e, VisitResult, Protocol::Env>
where
'ctx: 'this + 'visitor + 'hint;
/// Ask the walker for information about it's support of the protocol.
fn known<'a>(
&'a mut self,
hint: &'a WithLt<'a, Protocol::Hint>,
) -> NativeForm<'a, Result<WithLt<'a, Protocol::Known>, ()>, Protocol::Env>
where
WithLt<'a, Protocol::Known>: DynBind<Protocol::Env>;
}
#[derive(SendSync)]
pub struct DynVisitorWith<'temp, 'ctx, Protocol: ?Sized + HintMeta> {
visitor: DynVisitor<'temp, 'ctx, Protocol::Env>,
_marker: Marker<Protocol>,
}
impl<'temp, 'ctx: 'temp, Protocol: ?Sized + HintMeta> DynVisitorWith<'temp, 'ctx, Protocol> {
pub fn new<T>(visitor: &'temp mut T) -> Self
where
T: AnyTraitDynBind<'temp, 'ctx, Protocol::Env>,
{
Self {
visitor: DynVisitor(visitor),
_marker: Default::default(),
}
}
pub fn as_known(&mut self) -> &mut type_name::Lowered<'temp, 'ctx, Protocol> {
self.visitor
.upcast_mut::<type_name::Lowered<'temp, 'ctx, Protocol>>()
.unwrap()
}
pub fn into_inner(self) -> DynVisitor<'temp, 'ctx, Protocol::Env> {
self.visitor
}
}
impl<'temp, 'ctx, Protocol: ?Sized + HintMeta> Deref for DynVisitorWith<'temp, 'ctx, Protocol> {
type Target = DynVisitor<'temp, 'ctx, Protocol::Env>;
fn deref(&self) -> &Self::Target {
&self.visitor
}
}
impl<'temp, 'ctx, Protocol: ?Sized + HintMeta> DerefMut for DynVisitorWith<'temp, 'ctx, Protocol> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.visitor
}
}
const _: () = {
pub struct HintProto<Protocol: ?Sized>(Marker<Protocol>);
impl<'a, 'ctx, Protocol: ?Sized> type_name::Lower<'a, 'ctx, &'a &'ctx ()> for HintProto<Protocol>
where
Protocol: HintMeta,
{
type Lowered = dyn Hint<'ctx, Protocol> + 'a;
}
impl<'a, 'ctx, Protocol: ?Sized> type_name::Raise<'a, 'ctx, &'a &'ctx ()>
for dyn Hint<'ctx, Protocol> + 'a
where
Protocol: HintMeta,
{
type Raised = HintProto<Protocol>;
}
};
pub fn hint_protocol<
'ctx: 'walker + 'visitor + 'hint,
'walker: 'e,
'visitor: 'e,
'hint: 'e,
'e,
Protocol: ?Sized + type_name::WithLt<'static, 'static>,
E,
T,
>(
walker: DynWalker<'walker, 'ctx, E>,
visitor: &'visitor mut T,
hint: WithLt<'hint, <type_name::Raised<'static, 'static, Protocol> as HintMeta>::Hint>,
) -> NativeForm<'e, VisitResult<()>, E>
where
E: Environment,
T: AnyTrait<'ctx> + DynBind<E>,
type_name::Raised<'static, 'static, Protocol>: HintMeta<Env = E>,
{
if let Some(object) = walker
.0
.cast_mut2()
.upcast_mut::<dyn Hint<'ctx, type_name::Raised<'static, 'static, Protocol>> + '_>()
{
object
.hint(DynVisitorWith::new(visitor), hint)
.map((), |_, x| Into::into(x))
.cast()
} else {
E::value(VisitResult::Skipped(())).cast()
}
}