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
use derive_more::{Deref, From, IsVariant};

use crate::graph::GhostToken;
use crate::visitor::TraversingResult;

#[derive(Debug)]
pub enum Skip<E> {
    WithError(E),
    SideGuard,
}

#[derive(IsVariant, Clone, Ord, PartialOrd, Eq, PartialEq, Copy)]
#[repr(u8)]
pub enum VisitSide {
    Entering,
    Exiting,
    Leaf,
}

#[derive(From, Deref)]
#[deref(forward)]
pub struct MutAccess<'token>(&'token mut GhostToken);

#[derive(From, Deref)]
#[deref(forward)]
pub struct RefAccess<'token>(&'token GhostToken);

pub struct VisitGuard<N, Access>(N, VisitSide, Access);

pub type RefVisitGuard<'token, N> = VisitGuard<N, RefAccess<'token>>;
pub type MutVisitGuard<'token, N> = VisitGuard<N, MutAccess<'token>>;

impl<N, T> VisitGuard<N, T> {
    pub fn new<U: Into<T>>(node: N, side: VisitSide, access: U) -> Self {
        Self(node, side, access.into())
    }

    pub fn allow_only<E>(self, matches: VisitSide) -> Result<(N, T), Skip<E>> {
        self.1.guard(matches).map(|_| (self.0, self.2))
    }

    pub fn allow_all(self) -> (N, T, VisitSide) {
        (self.0, self.2, self.1)
    }
}

impl<'token, N> VisitGuard<N, MutAccess<'token>> {
    pub fn access(&mut self) -> &mut GhostToken {
        self.2 .0
    }
}

impl<'token, N> VisitGuard<N, RefAccess<'token>> {
    pub fn access(&self) -> &GhostToken {
        self.2 .0
    }
}

impl VisitSide {
    pub fn guard<E>(self, guarded: VisitSide) -> Result<(), Skip<E>> {
        if self != guarded {
            Err(Skip::SideGuard)
        } else {
            Ok(())
        }
    }
}

impl<E> From<E> for Skip<E> {
    fn from(value: E) -> Self {
        Skip::WithError(value)
    }
}

pub trait SkipExt<E> {
    fn skipped(self) -> Result<(), E>;
}

impl<E> SkipExt<E> for TraversingResult<E> {
    fn skipped(self) -> Result<(), E> {
        match self {
            Ok(_) => Ok(()),
            Err(Skip::SideGuard) => Ok(()),
            Err(Skip::WithError(e)) => Err(e),
        }
    }
}