//! 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::{Canonical, Effective},
environment::{Environment, InEnvironment},
higher_ranked::{Hrt, WithLt},
DynBind, 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<'src, Protocol: ?Sized + HintMeta>: DynBind<Protocol::Env>
where
for<'r> WithLt<'r, Protocol::Hint>: Sized,
for<'r> WithLt<'r, Protocol::Known>: DynBind<Protocol::Env> + Sized,
{
/// Hint to the walker to use the `P` protocol.
///
/// This should only be called once per [`RequestHint`].
fn hint<'r>(
&'r mut self,
visitor: DynVisitorWith<'r, 'src, Protocol>,
hint: WithLt<'r, Protocol::Hint>,
) -> Canonical<'r, VisitResult, Protocol::Env>;
/// Ask the walker for information about it's support of the protocol.
fn known<'r>(
&'r mut self,
hint: &'r WithLt<'r, Protocol::Hint>,
) -> Canonical<'r, Result<WithLt<'r, Protocol::Known>, ()>, Protocol::Env>;
}
#[derive(SendSync)]
pub struct DynVisitorWith<'r, 'src, Protocol: ?Sized + HintMeta> {
visitor: DynVisitor<'r, 'src, Protocol::Env>,
_marker: Marker<Protocol>,
}
impl<'r, 'src, Protocol: ?Sized + HintMeta> DynVisitorWith<'r, 'src, Protocol> {
pub fn new<T>(visitor: &'r mut T) -> Self
where
T: AnyTraitDynBind<'src, Protocol::Env>,
{
Self {
visitor: DynVisitor::new(visitor),
_marker: Default::default(),
}
}
pub fn as_known(&mut self) -> &mut type_name::Lowered<'_, 'src, Protocol> {
self.visitor
.upcast_mut::<type_name::Lowered<'_, 'src, Protocol>>()
.unwrap()
}
pub fn into_inner(self) -> DynVisitor<'r, 'src, Protocol::Env> {
self.visitor
}
}
impl<'r, 'src, Protocol: ?Sized + HintMeta> Deref for DynVisitorWith<'r, 'src, Protocol> {
type Target = DynVisitor<'r, 'src, Protocol::Env>;
fn deref(&self) -> &Self::Target {
&self.visitor
}
}
impl<'r, 'src, Protocol: ?Sized + HintMeta> DerefMut for DynVisitorWith<'r, 'src, Protocol> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.visitor
}
}
impl<'u, 'src, Protocol: ?Sized> type_name::Lower<'u, 'src, &'u &'src ()>
for dyn Hint<'static, Protocol>
where
Protocol: HintMeta,
{
type Lowered = dyn Hint<'src, Protocol> + 'u;
}
impl<'u, 'src, Protocol: ?Sized> type_name::Raise<'u, 'src, &'u &'src ()>
for dyn Hint<'src, Protocol> + 'u
where
Protocol: HintMeta,
{
type Raised = dyn Hint<'static, Protocol>;
}
pub fn hint_protocol<'r, 'src, Protocol: ?Sized + type_name::WithLt<'r, 'src>, E, T>(
walker: DynWalker<'r, 'src, E>,
visitor: &'r mut T,
hint: WithLt<'r, <type_name::Raised<'r, 'src, Protocol> as HintMeta>::Hint>,
) -> Canonical<'r, VisitResult<()>, E>
where
E: Environment,
T: AnyTrait<'src> + DynBind<E>,
type_name::Raised<'r, 'src, Protocol>: HintMeta<Env = E>,
for<'a> WithLt<'a, <type_name::Raised<'r, 'src, Protocol> as HintMeta>::Hint>: Sized,
for<'a> WithLt<'a, <type_name::Raised<'r, 'src, Protocol> as HintMeta>::Known>:
DynBind<E> + Sized,
{
if let Some(object) = walker
.into_inner()
.as_any_trait_mut()
.upcast_mut::<dyn Hint<'src, type_name::Raised<'r, 'src, Protocol>> + 'r>()
{
object
.hint(DynVisitorWith::new(visitor), hint)
.map((), |_, x| Into::into(x))
.cast()
} else {
E::value(VisitResult::Skipped(())).cast()
}
}