1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//! 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()
    }
}