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

use effectful::{
    effective::{Canonical, Effective}, environment::Environment, higher_ranked::{for_lt, Rank1}, DynBind, SendSync
};

use crate::{
    any::type_name, protocol::{walker::hint::HintMeta, DynVisitor}
};

use super::VisitResult;

/// Trait object for the [`Value`] protocol.
///
/// Types implementing the [`Value`] protocol will implement this trait.
pub trait Value<'src, T: ?Sized + type_name::Static, 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<'r>(
        &'r mut self,
        value: type_name::Lowered<'r, 'src, T>,
    ) -> Canonical<'r, VisitResult<type_name::Lowered<'r, 'src, T>>, E>
    where
        type_name::Lowered<'r, 'src, T>: Sized + DynBind<E>;
}

impl<'u, 'src, T: ?Sized, E> type_name::Lower<'u, 'src, &'u &'src ()> for dyn Value<'static, T, E>
where
    E: Environment,
    T: type_name::Static,
{
    type Lowered = dyn Value<'src, T, E> + 'u;
}

impl<'u, 'src, T: ?Sized, E> type_name::Raise<'u, 'src, &'u &'src ()> for dyn Value<'src, T, E> + 'u
where
    E: Environment,
    T: type_name::Static,
{
    type Raised = dyn Value<'static, T, E>;
}

// This enrolls the Value protocol into the walker hint system.
impl<T, E: Environment> HintMeta for dyn Value<'static, T, E>
where
    T: ?Sized + type_name::Static,
{
    type Known = for_lt!(<'b> ValueKnown<'b, T>);

    type Hint = Rank1<()>;
}

impl<T: ?Sized, E: Environment> effectful::environment::InEnvironment for dyn Value<'static, T, E> {
    type Env = E;
}

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

pub fn visit_value<
    'r, 'src,
    T,
    E,
>(
    visitor: DynVisitor<'r, 'src, E>,
    value: T,
) -> Canonical<'r, VisitResult<T>, E>
where
    E: Environment,
    T: type_name::WithLt<'r, 'src> + DynBind<E>,
    type_name::Raised<'r, 'src, T>: type_name::Static,
{
    if let Some(object) = visitor
        .into_inner()
        .as_any_trait_mut()
        .upcast_mut::<dyn Value<'src, type_name::Raised<'r, 'src, T>, 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(value)).cast()
    }
}