//! 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 crate::{
any::{AnyTrait, TypeName},
effect::{Effect, EffectiveExt as _, ErasedEffective, ReadyExt as _, Ss},
hkt::Marker,
protocol::{visitor::VisitResult, DynVisitor, 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: DynVisitorWith<'visitor, 'ctx, Protocol>,
hint: MetaHint<'hint, 'ctx, Protocol>,
) -> ErasedEffective<'e, VisitResult, 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 DynVisitorWith<'temp, 'ctx, Protocol: ?Sized> {
visitor: DynVisitor<'temp, 'ctx>,
_marker: Marker<Protocol>,
}
pub trait HasProtocol<Protocol: ?Sized> {}
impl<'temp, 'ctx: 'temp, Protocol: ?Sized + HintMeta> DynVisitorWith<'temp, 'ctx, Protocol> {
pub fn new<T>(visitor: &'temp mut T) -> Self
where
T: AnyTrait<'ctx> + HasProtocol<Protocol> + Ss,
{
Self {
visitor: DynVisitor(visitor),
_marker: Default::default(),
}
}
pub fn as_known(&mut self) -> &mut TypeName::T<'_, 'ctx, Protocol> {
self.visitor.upcast_mut::<Protocol>().unwrap()
}
pub fn into_inner(self) -> DynVisitor<'temp, 'ctx> {
self.visitor
}
}
impl<'temp, 'ctx, Protocol: ?Sized> Deref for DynVisitorWith<'temp, 'ctx, Protocol> {
type Target = DynVisitor<'temp, 'ctx>;
fn deref(&self) -> &Self::Target {
&self.visitor
}
}
impl<'temp, 'ctx, Protocol: ?Sized> DerefMut for DynVisitorWith<'temp, 'ctx, Protocol> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.visitor
}
}
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,
T,
>(
walker: DynWalker<'walker, 'ctx>,
visitor: &'visitor mut T,
hint: MetaHint<'hint, 'ctx, Protocol>,
) -> ErasedEffective<'e, VisitResult<()>, Protocol::Effect>
where
T: AnyTrait<'ctx> + HasProtocol<Protocol> + Ss,
{
if let Some(object) = walker.0.upcast_mut::<HintProto<Protocol>>() {
object
.hint(DynVisitorWith::new(visitor), hint)
.map(Into::into)
} else {
VisitResult::Skipped(()).ready()
}
}