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
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
#![no_std]

pub mod impls;
pub mod protocol;
pub mod protocols;

use core::fmt::Display;

use protocol::{AnyHint, AnyVisit, Protocol, ProtocolId};

/// Trait for all walker error types.
///
/// This gives an interface for visitors to generate their own errors.
/// `'value` is the lifetime of the source value which the error may borrow from.
pub trait Error<'value>: Sized + Display + 'value {
    /// Create a custom error.
    fn custom<T: Display>(message: T) -> Self;

    /// Create a custom error from a static string message.
    ///
    /// This is useful for error types that can't store arbitrary strings.
    /// The default impl forwards to `Self::custom`.
    fn custom_static_str(message: &'static str) -> Self {
        Self::custom(message)
    }

    /// Helper for making an error when a visitor is missing the
    /// visit for a protocol it hinted.
    fn missing_visit<P: Protocol<'value>>() -> Self {
        Self::custom_static_str("visitor is missing expected protocol")
    }
}

#[derive(Debug)]
pub struct BasicError(pub &'static str);

impl Display for BasicError {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.write_str(self.0)
    }
}

impl<'value> Error<'value> for BasicError {
    fn custom<T: Display>(_message: T) -> Self {
        Self("basic error")
    }

    fn custom_static_str(message: &'static str) -> Self {
        Self(message)
    }
}

/// Token value showing a hint was given.
pub struct HintGiven;

/// Status of a walker after walking using a visitor.
///
/// Some walkers can be walked multiple times to extract multiple
/// values.
pub enum WalkStatus {
    /// The walker is done.
    ///
    /// Attemping to call `walk` is likely to result in an error.
    Done,

    /// The walker can continue.
    Continue,
}

/// Walker over a value with lifetime `'value`.
pub trait Walker<'value> {
    /// Error type the walker generates.
    type Error: Error<'value>;

    /// Walk the walker over the value.
    ///
    /// This is the main entrypoint for walking a value.
    /// The walker should call [`Visit::visit`] on the provided visitor as it walks.
    ///
    /// The default impl calls [`Visitor::request_hint`] then returns an error if no hint
    /// was given. Self describing formats can replace the default impl to fall back to
    /// their own logic if no hint is given. It is recommended to keep the call to
    /// [`Visitor::request_hint`] before using walker specific logic to pick a protocol.
    fn walk(
        &mut self,
        visitor: &mut dyn Visitor<'value, Self::Error>,
    ) -> Result<WalkStatus, Self::Error> {
        // Request that the visitor give us a hint of what protocol to use.
        match visitor.request_hint(self.hints())? {
            Some(HintGiven) => Ok(WalkStatus::Done),
            None => Err(<Self::Error as Error>::custom("walker needs a hint")),
        }
    }

    /// Get the hints the walker supports.
    ///
    /// The hint lookup is seperate from [`Walker`] so that [`Visitor::request_hint`] can't
    /// easily cause a infinite loop be calling [`Walker::walk`].
    fn hints(&mut self) -> &mut dyn Hints<'value, Self::Error>;
}

/// Hint lookup for a walker.
pub trait Hints<'value, Err> {
    /// Query the walker for a given protocol.
    ///
    /// If the walker doesn't support the protocol then a `None` is returned.
    /// Note, a walker can return `None` if it can't handle a hint of the protocol for the given
    /// value.
    fn protocol(&mut self, id: ProtocolId) -> Option<AnyHint<'_, 'value, Err>> {
        let _ = id;
        None
    }
}

/// Visitor over a value to be built.
pub trait Visitor<'value, Err> {
    /// Request the visitor hint what protocol to use.
    ///
    /// It is not recommended to call this while in a protocol hint as a walker.
    /// Calling this method when already processing a hint can cause a infinite loop.
    ///
    /// The visitor will hint the protocol by calling the [`Hint::hint`] method on the
    /// the walker's returned hint instance for the protocol.
    ///
    /// A return value of `Ok(None)` means no hint was given to the walker.
    fn request_hint(
        &mut self,
        hints: &mut dyn Hints<'value, Err>,
    ) -> Result<Option<HintGiven>, Err> {
        let _ = hints;
        Ok(None)
    }

    /// Query the visitor for a given protocol.
    ///
    /// If the visitor doesn't support the protocol then a `None` is returned.
    fn protocol(&mut self, id: ProtocolId) -> Option<AnyVisit<'_, 'value, Err>> {
        let _ = id;
        None
    }
}

pub type HintOps<'walking, 'value, P, Err> = &'walking mut dyn Hint<'value, P, Err>;
pub type VisitOps<'walking, 'value, P, Err> = &'walking mut dyn Visit<'value, P, Err>;

/// Protocol specific hint for a walker.
pub trait Hint<'value, P: Protocol<'value>, Err> {
    /// Hint that protocol `P` should be used.
    ///
    /// After hinting a protocol, a walker should invoke only the same protocol on the visitor.
    /// This is not forced though.
    fn hint(
        &mut self,
        visitor: &mut dyn Visitor<'value, Err>,
        hint: P::Hint,
    ) -> Result<HintGiven, Err>;

    /// Any information the walker has for the protocol.
    ///
    /// This information should be easy to get inside the walker, and should
    /// only be used when making a decision of what protocol to hint as a visitor.
    /// This can be helpful for doing things like preallocating space in the visitor.
    ///
    /// Most protocols will allow returning a value representing no knowledge is known by the
    /// walker.
    fn known(&mut self, hint: &P::Hint) -> Result<P::Known, Err>;
}

/// Protocol specific visit for a visitor.
pub trait Visit<'value, P: Protocol<'value>, Err: Error<'value>> {
    /// Visit a value from the walker.
    fn visit<'walking>(&'walking mut self, accessor: P::Accessor<'walking, Err>)
        -> Result<(), Err>;
}

/// A type buildable from a walker.
pub trait Buildable<'value, Err>: Sized {
    /// The builder that can be used to build a value.
    type Builder: Builder<'value, Err, Value = Self>;
}

/// Build a [`Buildable`] type from a walker.
///
/// This calls [`Walker::walk`] on the walker.
pub fn build<'value, T: Buildable<'value, Err>, Err, W: ?Sized + Walker<'value, Error = Err>>(
    walker: &mut W,
) -> Result<(T, WalkStatus), W::Error> {
    let mut builder = T::Builder::init();
    let status = walker.walk(builder.as_visitor())?;
    Ok((builder.finish()?, status))
}

/// Extension to [`Visitor`] that allows constructing and finishing the visitor.
pub trait Builder<'value, Err> {
    /// Type to be built.
    type Value: Sized;

    /// Init a new builder.
    fn init() -> Self
    where
        Self: Sized;

    /// As a visitor.
    fn as_visitor(&mut self) -> &mut dyn Visitor<'value, Err>;

    /// Finish the value.
    fn finish(self) -> Result<Self::Value, Err>
    where
        Self: Sized;
}

/// A walkable type.
pub trait WalkableRef<'walking, 'value>: Walkable<'walking, 'value> {
    /// Create a walker for a value.
    fn walker_ref(&'walking self) -> Self::Walker;
}

/// A walkable type.
pub trait Walkable<'walking, 'value> {
    /// The walker for the type.
    type Walker: Walker<'value>;

    /// Create a walker for a value.
    ///
    /// In general, calling this multiple times will result in different walks.
    fn walker(&'walking mut self) -> Self::Walker;
}

/// Helper to lookup a protocol's hint on a walker.
///
/// A `Ok(None)` is returned if the walker doesn't support the protocol.
/// A `Err(...)` is returned if the walker returns a hint instance not for the protocol.
pub fn lookup_hint<
    'value,
    P: Protocol<'value>,
    Err: Error<'value>,
    H: ?Sized + Hints<'value, Err>,
>(
    hints: &mut H,
) -> Result<Option<HintOps<'_, 'value, P, Err>>, Err> {
    match hints.protocol(ProtocolId::of::<P>()) {
        Some(hint) => match hint.downcast::<P>() {
            Ok(hint) => Ok(Some(hint)),
            Err(_) => Err(Err::custom_static_str(
                "unexpected protocol hint instance from walker",
            )),
        },
        None => Ok(None),
    }
}

/// Helper to lookup a protocol's visit on a visitor.
///
/// A `Ok(None)` is returned if the visitor doesn't support the protocol.
/// A `Err(...)` is returned if the visitor returns a visit instance not for the protocol.
pub fn lookup_visit<
    'value,
    P: Protocol<'value>,
    Err: Error<'value>,
    V: ?Sized + Visitor<'value, Err>,
>(
    visitor: &mut V,
) -> Result<Option<VisitOps<'_, 'value, P, Err>>, Err> {
    match visitor.protocol(ProtocolId::of::<P>()) {
        Some(hint) => match hint.downcast::<P>() {
            Ok(hint) => Ok(Some(hint)),
            Err(_) => Err(Err::custom_static_str(
                "unexpected protocol visit instance from visitor",
            )),
        },
        None => Ok(None),
    }
}

pub fn walking_clone<'walking, 'value, T>(
    value: &'walking T,
) -> Result<T, <T::Walker as Walker<'value>>::Error>
where
    T: Buildable<'value, <T::Walker as Walker<'value>>::Error> + WalkableRef<'walking, 'value>,
{
    let (value, _) = build(&mut value.walker_ref())?;
    Ok(value)
}