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
#![cfg_attr(not(feature = "std"), no_std)]

#[cfg(feature = "alloc")]
extern crate alloc;

pub mod build;
pub mod impls;
pub mod protocol;
pub mod protocols;
pub mod transform;
pub mod walk;

use protocol::{AnyHint, AnyVisit, Hint, ProtocolId, Visit};

/// Signal to a walker or visitor that it should exit early or continue.
///
/// [`Walker`] and [`Visitor`] can't signal an error condition with a
/// [`Result`] so instead they can use this.
#[must_use]
#[derive(Copy, Clone, Debug)]
pub enum ControlFlow {
    /// Continue the walk as normal.
    Continue,

    Done,

    Error,
}

impl ControlFlow {
    pub fn to_done(self) -> Self {
        match self {
            ControlFlow::Continue => ControlFlow::Done,
            ControlFlow::Done => ControlFlow::Done,
            ControlFlow::Error => ControlFlow::Error,
        }
    }

    pub fn to_continue(self) -> Self {
        match self {
            ControlFlow::Continue => ControlFlow::Continue,
            ControlFlow::Done => ControlFlow::Continue,
            ControlFlow::Error => ControlFlow::Error,
        }
    }
}

/// Walker over a value.
///
/// The walker can give out borrows with a lifetime of `'ctx` to the visitor.
pub trait Walker<'ctx> {
    /// Walk the value.
    ///
    /// The walker will call [`Visit::visit`] methods for one or more
    /// [`Protocol`][protocol::Protocol] on the given `visitor`.
    fn walk(&mut self, visitor: &mut dyn Visitor<'ctx>) -> ControlFlow;
}

/// Hint lookup for a walker.
///
/// Some walkers may need a hint for what the value it's walking actually is.
/// To support this, a [`Walker`] will call the visitor's [`Visitor::request_hint`]
/// method and the visitor can call a [`Hint::hint`] method for a
/// [`Protocol`][protocol::Protocol] that the walker supports.
pub trait WalkerHints<'ctx> {
    /// Query the walker for a given protocol.
    ///
    /// If the walker doesn't support the protocol then a `None` is returned.
    /// Note, a walker may return a `None` for a particular value but support
    /// the protocol for other values.
    ///
    /// Use [`lookup_hint`][protocol::lookup_hint], or manually call
    /// [`AnyHint::downcast`] on the returned value to access the [`Hint`].
    fn protocol(&mut self, id: ProtocolId) -> Option<AnyHint<'_, 'ctx>>;
}

/// Visitor over a value to be built.
pub trait Visitor<'ctx> {
    /// Request the visitor provide a hint for what protocol to use.
    ///
    /// Some walkers may need a hint for what the value it's walking actually is.
    /// To support this, a [`Walker`] will call this method
    /// and the visitor can call a [`Hint::hint`] method for a
    /// [`Protocol`][protocol::Protocol] that the walker supports.
    ///
    /// Returning a `None` means the visitor gave no hint. The `need_hint` will
    /// be `true` if the walker needs a hint to not encounter an error.
    fn request_hint(
        &mut self,
        hints: &mut dyn WalkerHints<'ctx>,
        need_hint: bool,
    ) -> ControlFlow;

    /// Query the visitor for a given protocol.
    ///
    /// If the visitor doesn't support the protocol then a `None` is returned.
    /// Note, a visitor may return a `None` if it doesn't support the protocol
    /// for it's current internal state but could otherwise.
    ///
    /// Use [`lookup_visit`][protocol::lookup_visit], or manually call
    /// [`AnyVisit::downcast`] on the returned value to access the [`Visit`].
    fn protocol(&mut self, id: ProtocolId) -> Option<AnyVisit<'_, 'ctx>>;
}