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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
use core::any::TypeId;

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

use crate::{
    any::type_name,
    hkt::Marker,
    protocol::{walker::hint::HintMeta, DynVisitor},
    symbol::Symbol,
    walk::{DynWalkerAdapter, DynWalkerError, DynWalkerObjSafe},
    Walker,
};

use super::VisitResult;

pub mod tags {
    use super::*;

    pub type Struct = TagConst<{ Symbol::new("Struct").to_int() }>;
    pub type Map = TagConst<{ Symbol::new("Map").to_int() }>;
    pub type Variant = TagConst<{ Symbol::new("Variant").to_int() }>;
    pub type Key = TagConst<{ Symbol::new("Key").to_int() }>;
    pub type Value = TagConst<{ Symbol::new("Value").to_int() }>;
    pub type FieldNames = TagConst<{ Symbol::new("Field Names").to_int() }>;
    pub type TypeName = TagConst<{ Symbol::new("Type Name").to_int() }>;
    pub type TypeId = TagConst<{ Symbol::new("Type ID").to_int() }>;
}

pub trait TagKind<E: SsBound>: Copy + DynBind<E> + 'static {
    fn symbol(&self) -> Symbol;
}

pub trait ConstTagKind<E: SsBound>: TagKind<E> {
    const NEW: Self;
}

impl<E: SsBound, const SYMBOL: u64> ConstTagKind<E> for TagConst<SYMBOL> {
    const NEW: Self = TagConst;
}

#[derive(Copy, Clone, SendSync)]
pub struct TagConst<const SYMBOL: u64>;

#[derive(Copy, Clone, SendSync)]
pub struct TagDyn(pub Symbol);

impl<const SYMBOL: u64, E: SsBound> TagKind<E> for TagConst<SYMBOL> {
    fn symbol(&self) -> Symbol {
        Symbol::from_int(SYMBOL)
    }
}

impl<const SYMBOL: u64> TagConst<SYMBOL> {
    pub const VALUE: Symbol = Symbol::from_int(SYMBOL);
}

impl<E: SsBound> TagKind<E> for TagDyn {
    fn symbol(&self) -> Symbol {
        self.0
    }
}

pub trait Tag<'src, K: TagKind<E>, E: Environment>: DynBind<E> {
    fn visit<'r>(
        &'r mut self,
        kind: K,
        walker: DynWalkerObjSafe<'r, 'src, E>,
    ) -> Canonical<'r, VisitResult, E>;
}

impl<'u, 'src, K: TagKind<E>, E> type_name::Lower<'u, 'src, &'u &'src ()> for dyn Tag<'static, K, E>
where
    E: Environment,
{
    type Lowered = dyn Tag<'src, K, E> + 'u;
}

impl<'u, 'src, K: TagKind<E>, E> type_name::Raise<'u, 'src, &'u &'src ()>
    for dyn Tag<'src, K, E> + 'u
where
    E: Environment,
{
    type Raised = dyn Tag<'static, K, E>;
}

impl<K: TagKind<E>, E: Environment> HintMeta for dyn Tag<'static, K, E> {
    type Known = Rank1<TagKnown>;

    type Hint = Rank1<TagHint<K>>;
}

impl<K: TagKind<E>, E: Environment> effectful::environment::InEnvironment
    for dyn Tag<'static, K, E>
{
    type Env = E;
}

#[derive(SendSync)]
pub struct TagKnown {
    pub kind_available: Option<bool>,
}

#[derive(SendSync)]
pub struct TagHint<K> {
    pub kind: K,
}

#[derive(Debug, PartialEq, Clone, Copy, SendSync)]
pub enum TagErrorKind<E> {
    NeverWalked,

    /// This can only happen if a panic happens during the walk and is then caught before calling
    /// finish.
    WalkNeverFinished,

    Walker(E),

    SkippedWasWalked,
}

#[derive(Debug, PartialEq, Copy, Clone, SendSync)]
#[allow(unused)]
pub struct TagError<E> {
    symbol: Symbol,
    err: TagErrorKind<E>,
}

impl<E> TagError<E> {
    fn new<K: TagKind<Env>, Env: SsBound>(tag: K, err: E) -> Self {
        Self {
            symbol: tag.symbol(),
            err: TagErrorKind::Walker(err),
        }
    }

    fn never_walked<K: TagKind<Env>, Env: SsBound>(tag: K) -> Self {
        Self {
            symbol: tag.symbol(),
            err: TagErrorKind::NeverWalked,
        }
    }

    fn walk_never_finished<K: TagKind<Env>, Env: SsBound>(tag: K) -> Self {
        Self {
            symbol: tag.symbol(),
            err: TagErrorKind::WalkNeverFinished,
        }
    }

    fn was_walked<K: TagKind<Env>, Env: SsBound>(tag: K) -> Self {
        Self {
            symbol: tag.symbol(),
            err: TagErrorKind::SkippedWasWalked,
        }
    }
}

#[inline(always)]
pub fn visit_tag<'r, 'src, K: TagKind<E>, E: Environment, W: crate::Walker<'src, E> + 'r>(
    kind: K,
    visitor: DynVisitor<'r, 'src, E>,
    walker: W,
) -> Canonical<'r, Result<VisitResult<W>, TagError<W::Error>>, E> {
    // Wrap the walker to allow it to be passed to a dyn walker argument.
    let walker = DynWalkerAdapter::new(walker);

    // Give everything to the effective chain to be used as context.
    E::value((kind, visitor, walker))
        .update_map((), |_, (kind, visitor, walker)| {
            // Try to visit the tag kind as given.
            tri!(visitor.upcast_mut::<dyn Tag<'src, K, E>>())
                .visit(*kind, walker)
                .map((), |_, x| VisitResult::to_flow(x))
                .cast()
        })
        .or_else_update((), |_, (kind, visitor, walker), _| {
            // If the tag type is already dynamic we don't need to try visiting again.
            if TypeId::of::<K>() == TypeId::of::<TagDyn>() {
                return E::value(None).cast();
            }

            // Visit using the dynamic tag, but with the same symbol.
            tri!(visitor.upcast_mut::<dyn Tag<'src, TagDyn, E>>())
                .visit(TagDyn(kind.symbol()), walker)
                .map((), |_, x| VisitResult::to_flow(x))
                .cast()
        })
        .map((), |_, ((kind, _, walker), result)| match result {
            // The visit was performed so return the control flow.
            Some(flow) => Ok(VisitResult::Control(flow)),

            // The visit was not performed.
            // We need to give back the walker as we got it.
            None => match walker.into_inner() {
                Ok(walker) => Ok(VisitResult::Skipped(walker)),
                Err(err) => Err(map_walker_err(kind, err)),
            },
        })
        .cast()
}

fn map_walker_err<'ctx, K: TagKind<E>, W: Walker<'ctx, E>, E: Environment>(
    kind: K,
    err: DynWalkerError<'ctx, W, E>,
) -> TagError<W::Error> {
    match err {
        DynWalkerError::Walker(err) => TagError::new(kind, err),
        DynWalkerError::NeverWalked(_) => TagError::never_walked(kind),
        DynWalkerError::WalkNeverFinished => TagError::walk_never_finished(kind),
        DynWalkerError::WasWalked(_) => TagError::was_walked(kind),
    }
}