//! 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 crate::{
any::TypeName,
effect::{Effect, EffectiveExt as _, ErasedEffective, ReadyExt as _},
hkt::Marker,
protocol::{visitor::VisitResult, DynWalker},
Flow,
};
#[allow(non_snake_case)]
pub mod Meta {
pub trait MemberTypeForLt<'a, 'ctx: 'a, B> {
type T: ?Sized + LowerTypeWithBound<'a, 'ctx, &'a &'ctx (), Higher = Self>;
}
pub trait MemberType: for<'a, 'ctx> MemberTypeForLt<'a, 'ctx, &'a &'ctx ()> {}
impl<T: ?Sized> MemberType for T where T: for<'a, 'ctx> MemberTypeForLt<'a, 'ctx, &'a &'ctx ()> {}
pub trait LowerTypeWithBound<'a, 'ctx: 'a, B>: 'a + Send + Sync + Sized {
type Higher: ?Sized + MemberTypeForLt<'a, 'ctx, &'a &'ctx (), T = Self> + MemberType;
}
pub trait LowerType<'a, 'ctx: 'a>: LowerTypeWithBound<'a, 'ctx, &'a &'ctx ()> {}
impl<'a, 'ctx: 'a, T: ?Sized> LowerType<'a, 'ctx> for T where
T: LowerTypeWithBound<'a, 'ctx, &'a &'ctx ()>
{
}
pub type T<'a, 'ctx, __> = <__ as MemberTypeForLt<'a, 'ctx, &'a &'ctx ()>>::T;
pub type HigherRanked<'a, 'ctx, __> =
<__ as LowerTypeWithBound<'a, 'ctx, &'a &'ctx ()>>::Higher;
}
impl<'a, 'ctx> Meta::MemberTypeForLt<'a, 'ctx, &'a &'ctx ()> for () {
type T = ();
}
impl<'a, 'ctx: 'a> Meta::LowerTypeWithBound<'a, 'ctx, &'a &'ctx ()> for () {
type Higher = ();
}
/// Meta information for the hint.
///
/// This gives the visitor more information to work from when selecting a hint.
pub trait HintMeta: TypeName::MemberType + Send + Sync + '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: Meta::MemberType;
/// Extra information the visitor can give to the walker about what it is expecting.
type Hint: Meta::MemberType;
type Effect: Effect;
}
pub type MetaKnown<'a, 'ctx, Protocol> = Meta::T<'a, 'ctx, <Protocol as HintMeta>::Known>;
pub type MetaHint<'a, 'ctx, Protocol> = Meta::T<'a, 'ctx, <Protocol as HintMeta>::Hint>;
/// Object implementing the [`Hint`] protocol.
pub trait Hint<'ctx, Protocol: ?Sized + HintMeta> {
/// 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: &'visitor mut TypeName::T<'visitor, 'ctx, Protocol>,
hint: MetaHint<'hint, 'ctx, Protocol>,
) -> ErasedEffective<'e, Flow, Protocol::Effect>
where
'ctx: 'this + 'visitor + 'hint + 'e;
/// Ask the walker for information about it's support of the protocol.
fn known<'a>(
&'a mut self,
hint: &'a MetaHint<'a, 'ctx, Protocol>,
) -> ErasedEffective<'a, Result<MetaKnown<'a, 'ctx, Protocol>, ()>, Protocol::Effect>;
}
pub struct HintProto<Protocol: ?Sized>(Marker<Protocol>);
impl<'a, 'ctx, Protocol: ?Sized> TypeName::MemberTypeForLt<'a, 'ctx, &'a &'ctx ()>
for HintProto<Protocol>
where
Protocol: HintMeta,
{
type T = dyn Hint<'ctx, Protocol> + Send + Sync + 'a;
}
impl<'a, 'ctx, Protocol: ?Sized> TypeName::LowerTypeWithBound<'a, 'ctx, &'a &'ctx ()>
for dyn Hint<'ctx, Protocol> + Send + Sync + 'a
where
Protocol: HintMeta,
{
type Higher = HintProto<Protocol>;
}
pub fn hint_protocol<
'ctx,
'walker: 'e,
'visitor: 'e,
'hint: 'e,
'e,
Protocol: ?Sized + HintMeta,
>(
walker: DynWalker<'walker, 'ctx>,
visitor: &'visitor mut TypeName::T<'visitor, 'ctx, Protocol>,
hint: MetaHint<'hint, 'ctx, Protocol>,
) -> ErasedEffective<'e, VisitResult<()>, Protocol::Effect> {
if let Some(object) = walker.0.upcast_mut::<HintProto<Protocol>>() {
object.hint(visitor, hint).map(Into::into)
} else {
VisitResult::Skipped(()).ready()
}
}