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
//! 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::marker::PhantomData;

use crate::{
    any::{TypeName, TypeNameable},
    nameable,
    protocol::Implementer,
};

/// Meta information for the hint.
///
/// This gives the visitor more information to work from when selecting a hint.
pub trait Meta<'ctx> {
    /// Information known by the walker.
    ///
    /// This should be information easy to get without changing the state of the walker
    /// in an irreversable way.
    type Known<'a>;

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

/// Object implementing the [`Hint`] protocol.
pub trait Hint<'ctx, P: Meta<'ctx>> {
    /// Hint to the walker to use the `P` protocol.
    ///
    /// This should only be called once per [`RequestHint`].
    fn hint(&mut self, visitor: &mut dyn Implementer<'ctx>, hint: P::Hint<'_>) -> Result<(), ()>;

    /// Ask the walker for information about it's support of the protocol.
    fn known(&mut self, hint: &P::Hint<'_>) -> Result<P::Known<'_>, ()>;
}

// nameable!(['a, 'ctx, P: Meta<'ctx> + TypeNameable<'a, 'ctx>]: dyn Hint<'ctx, P> + 'a => dyn Hint<'static, P::Name>);

const _: () = {
    impl<'a, 'ctx: 'a, P: TypeNameable<'a, 'ctx>>
        crate::any::TypeNameable<'a, 'ctx> for dyn Hint<'ctx, P> + 'a
    where
        P: ?Sized
    // where
    //     P: Meta<'ctx>
    {
        type Name = Name<P::Name>;
    }

    pub struct Name<T: ?Sized>(PhantomData<fn() -> *const T>);

    impl<'a, 'ctx: 'a, T: TypeName<'a, 'ctx>> crate::any::TypeName<'a, 'ctx> for Name<T>
    where
        T: ?Sized
    // where
    //     T::Nameable: Meta<'ctx>,
    {
        type Nameable = dyn Hint<'ctx, T::Nameable> + 'a;
    }
};

#[cfg(test)]
mod test {
    use crate::any::LtTypeId;

    use super::*;

    #[test]
    fn demo() {
        struct X;
        struct Y;

        nameable!(['a, 'ctx]: Y => Y);

        impl<'ctx, X> Hint<'ctx, Y> for X {
            fn hint(
                &mut self,
                visitor: &mut dyn Implementer<'ctx>,
                hint: <Y as Meta>::Hint<'_>,
            ) -> Result<(), ()> {
                todo!()
            }

            fn known(
                &mut self,
                hint: &<Y as Meta>::Hint<'_>,
            ) -> Result<<Y as Meta>::Known<'_>, ()> {
                todo!()
            }
        }

        impl<'ctx> Meta<'ctx> for Y {
            type Known<'a> = ();

            type Hint<'a> = ();
        }

        let x = X;
        let y: &dyn Hint<Y> = &x;

        fn id<'a, 'ctx, T: ?Sized + TypeNameable<'a, 'ctx>>(x: &T) {
            dbg!(LtTypeId::of::<T>());
        }

        id(y);

        todo!();
    }
}