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
//! [`Protocol`] for giving a visitor an owned value.
//!
//! In some sense, this is the most basic protocol.

use effectful::{
    bound::{Bool, Dynamic, IsSend, IsSync},
    effective::Effective,
    environment::{DynBind, EnvConfig, Environment, NativeForm},
    SendSync,
};

use crate::{
    any::TypeName,
    hkt::Marker,
    protocol::{
        walker::hint::{HasProtocol, HintMeta, Meta},
        DynVisitor,
    },
};

use super::VisitResult;

/// Trait object for the [`Value`] protocol.
///
/// Types implementing the [`Value`] protocol will implement this trait.
pub trait Value<'ctx, T: ?Sized + TypeName::MemberType<E>, E: Environment>: DynBind<E> {
    /// Visit a value of type `T`.
    ///
    /// Use this to give a value to a visitor. Its expected that a walker
    /// only calls this once per usage of the trait object, but that is not
    /// forced.
    ///
    /// If a [`ControlFlow::Break`] is returned then the walker
    /// should stop walking as soon as possible as there has likely been
    /// and error.
    fn visit<'a>(
        &'a mut self,
        value: TypeName::T<'a, 'ctx, T, E>,
    ) -> NativeForm<'a, VisitResult<Dynamic<TypeName::T<'a, 'ctx, T, E>>>, E>
    where
        Dynamic<TypeName::T<'a, 'ctx, T, E>>: DynBind<E>,
        TypeName::T<'a, 'ctx, T, E>: Sized,
        'ctx: 'a;
}

#[derive(SendSync)]
pub struct ValueProto<T: ?Sized + TypeName::MemberType<E>, E: Environment>(Marker<(*const T, E)>);

impl<'a, 'ctx, T: ?Sized, E> TypeName::MemberTypeForLt<'a, 'ctx, E, &'a &'ctx ()>
    for ValueProto<T, E>
where
    E: Environment,
    T: TypeName::MemberType<E>,
{
    type T = dyn Value<'ctx, T, E> + 'a;
}

impl<'a, 'ctx, T: ?Sized, E> TypeName::LowerTypeWithBound<'a, 'ctx, E, &'a &'ctx ()>
    for dyn Value<'ctx, T, E> + 'a
where
    E: Environment,
    T: TypeName::MemberType<E>,
{
    type Higher = ValueProto<T, E>;
}

#[derive(Copy, Clone, PartialEq, Debug, SendSync)]
pub struct ValueKnown<'a, T: ?Sized> {
    /// A preview of the value.
    ///
    /// This can be used to inspect the value before committing to a visit.
    pub preview: Option<Dynamic<&'a T>>,
}

#[derive(Copy, Clone, Debug, SendSync)]
pub struct ValueKnownHrt<T: ?Sized>(Marker<T>);

impl<'a, 'ctx, E: EnvConfig, T> Meta::MemberTypeForLt<'a, 'ctx, E, &'a &'ctx ()>
    for ValueKnownHrt<T>
where
    T: ?Sized + TypeName::MemberTypeForLt<'a, 'ctx, E, &'a &'ctx ()>,
    Dynamic<&'a TypeName::T<'a, 'ctx, T, E>>: DynBind<E>,
{
    type T = ValueKnown<'a, TypeName::T<'a, 'ctx, T, E>>;
}

impl<'a, 'ctx, E: EnvConfig, T: ?Sized> Meta::LowerTypeWithBound<'a, 'ctx, E, &'a &'ctx ()>
    for ValueKnown<'a, T>
where
    T: TypeName::LowerType<'a, 'ctx, E>,
    Dynamic<&'a T>: DynBind<E>,
{
    type Higher = ValueKnownHrt<TypeName::HigherRanked<'a, 'ctx, T, E>>;
}

// This enrolls the Value protocol into the walker hint system.
impl<T: TypeName::MemberType<E>, E: Environment> HintMeta for ValueProto<T, E>
where
    for<'a, 'ctx> Dynamic<&'a TypeName::T<'a, 'ctx, T, E>>: DynBind<E>,
{
    type Known = ValueKnownHrt<T>;

    type Hint = ();

    type Effect = E;
}

pub fn visit_value<
    'ctx: 'visitor,
    'visitor: 'e,
    'e,
    T: TypeName::LowerType<'e, 'ctx, E>,
    E: Environment,
>(
    visitor: DynVisitor<'visitor, 'ctx, E>,
    value: T,
) -> NativeForm<'e, VisitResult<Dynamic<T>>, E>
where
    TypeName::HigherRanked<'e, 'ctx, T, E>: TypeName::MemberType<E>,
    Dynamic<T>: DynBind<E>,
{
    if let Some(object) = visitor
        .0
        .upcast_mut::<ValueProto<TypeName::HigherRanked<'e, 'ctx, T, E>, E>>()
    {
        // Allow the visitor to give a hint if it wants.
        object.visit(value)
    } else {
        // If the visitor doesn't support request hint then we continue.
        E::value(VisitResult::Skipped(Dynamic(value))).cast()
    }
}

impl<'ctx, T, U: TypeName::MemberType<E>, E: Environment> HasProtocol<ValueProto<U, E>> for T where
    T: Value<'ctx, U, E>
{
}