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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//! 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> {
    /// 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>
    where
        WithLt<'r, Protocol::Known>: DynBind<Protocol::Env> + Sized;
}

#[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>,
    WithLt<'r, <type_name::Raised<'r, 'src, Protocol> as HintMeta>::Hint>: 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()
    }
}