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
//! 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::{MaybeSized, TypeName},
    bijective_higher_ranked_type,
    effect::{Effect, Future},
    hkt::AnySend,
    protocol::Visitor,
    Flow,
};

/// Meta information for the hint.
///
/// This gives the visitor more information to work from when selecting a hint.
pub trait HintMeta<'ctx> {
    /// 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: AnySend::Trait<'ctx>;

    /// Extra information the visitor can give to the walker about what it is expecting.
    type Hint;
}

pub type Known<'a, 'ctx, Protocol> = AnySend::T<'a, 'ctx, <Protocol as HintMeta<'ctx>>::Known>;

/// Object implementing the [`Hint`] protocol.
pub trait Hint<'ctx, Protocol: ?Sized + HintMeta<'ctx>, E: Effect<'ctx>> {
    /// Hint to the walker to use the `P` protocol.
    ///
    /// This should only be called once per [`RequestHint`].
    fn hint<'a>(
        &'a mut self,
        visitor: Visitor<'a, 'ctx>,
        hint: <Protocol as HintMeta<'ctx>>::Hint,
    ) -> Future<'a, 'ctx, Flow, E>;

    /// Ask the walker for information about it's support of the protocol.
    fn known<'a>(
        &'a mut self,
        hint: &'a <Protocol as HintMeta<'ctx>>::Hint,
    ) -> Future<'a, 'ctx, Result<Known<'a, 'ctx, Protocol>, ()>, E>;
}

bijective_higher_ranked_type! {
    pub type DynHint['ctx][Protocol, E]: MaybeSized['ctx][]
    for<'a>
    (dyn Hint<'ctx, Protocol, E> + Send + 'a)
    where {
        E: Effect<'ctx>,
        Protocol: ?Sized + MaybeSized::Trait<'ctx> + 'ctx,
    }
}

bijective_higher_ranked_type! {
    pub type [][E][Protocol[][]]: TypeName[][]
    for<'ctx>
    (DynHint<'ctx, TypeName::T<'ctx, Protocol>, E>)
    (DynHint<'ctx, Protocol, E>)
    where {
        E: Effect<'ctx>,
        Protocol: ?Sized,
    }
}

#[cfg(test)]
mod test {
    use crate::{
        effect::{BlockOn, Blocking, Spin},
        higher_ranked_type,
    };

    use super::*;

    #[test]
    fn demo() {
        struct X<'ctx>(&'ctx mut i32);

        #[derive(Debug)]
        struct Y;

        bijective_higher_ranked_type! {
            type DynY['ctx][]: MaybeSized['ctx][] for<'a> (Y)
        }

        bijective_higher_ranked_type! {
            type [][]: TypeName[][] for<'ctx> (DynY<'ctx>)
        }

        impl<'ctx, E: Effect<'ctx>> Hint<'ctx, DynY<'ctx>, E> for X<'ctx> {
            fn hint<'a>(
                &'a mut self,
                _visitor: Visitor<'a, 'ctx>,
                _hint: <DynY<'ctx> as HintMeta<'ctx>>::Hint,
            ) -> Future<'a, 'ctx, Flow, E> {
                todo!()
            }

            fn known<'a>(
                &'a mut self,
                _hint: &'a <DynY<'ctx> as HintMeta<'ctx>>::Hint,
            ) -> Future<'a, 'ctx, Result<Known<'a, 'ctx, DynY<'ctx>>, ()>, E> {
                E::ready(Ok(&mut *self.0))
            }
        }

        higher_ranked_type! {
            type KnownHkt['ctx]: (AnySend) = for<'lt> &'lt mut i32
        }

        impl<'ctx> HintMeta<'ctx> for DynY<'ctx> {
            type Known = KnownHkt<'ctx>;

            type Hint = ();
        }

        let mut z = 42;
        let mut x = X(&mut z);
        let y: &mut MaybeSized::T<'_, '_, DynHint<'_, DynY<'_>, Blocking>> = &mut x;

        fn id<'a, 'ctx, T: ?Sized + TypeName::Member<'ctx>>(_x: &MaybeSized::T<'a, 'ctx, T>) {}
        id::<DynHint<'_, DynY<'_>, Blocking>>(y);

        let x = Spin::block_on(y.known(&()));
        match x {
            Ok(value) => *value += 1,
            Err(_) => todo!(),
        }
        assert_eq!(z, 43);
    }
}