dangerous 0.10.0

Safely and explicitly parse untrusted / dangerous data
Documentation
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
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
use core::any::Any;

use crate::fmt;
use crate::input::{Input, MaybeString, Span};

use super::WithContext;

/// Information surrounding an error.
pub trait Context: 'static + Send + Sync {
    /// Returns the [`Span`] of input the error occurred if known.
    fn span(&self) -> Option<Span> {
        None
    }

    /// Returns the operation that failed in this context.
    fn operation(&self) -> &dyn Operation;

    /// Returns `true` if there is an expected value.
    fn has_expected(&self) -> bool {
        false
    }

    /// The expected value.
    ///
    /// # Errors
    ///
    /// Returns a [`fmt::Error`] if failed to write to the formatter.
    fn expected(&self, _w: &mut dyn fmt::Write) -> fmt::Result {
        Err(fmt::Error)
    }

    /// Returns `true` if the context belongs to a parent operation.
    ///
    /// This is used in adding external backtraces.
    fn is_child(&self) -> bool {
        false
    }
}

/// Operation that failed within a context.
pub trait Operation: Any + Send + Sync {
    /// Description of the operation in a simple manner, for informing a user
    /// what is trying to be achieved.
    ///
    /// The description should make sense in the following sentence if you were
    /// to substitute it:
    ///
    /// ```text
    /// error attempting to <operation>.
    /// ```
    ///
    /// # Errors
    ///
    /// Returns a [`fmt::Error`] if failed to write to the formatter.
    fn description(&self, w: &mut dyn fmt::Write) -> fmt::Result;

    /// Return a reference of self as [`Any`].
    fn as_any(&self) -> &dyn Any;
}

///////////////////////////////////////////////////////////////////////////////
// Basic expected context

impl Context for &'static str {
    fn operation(&self) -> &dyn Operation {
        &CoreOperation::Context
    }

    fn has_expected(&self) -> bool {
        true
    }

    fn expected(&self, w: &mut dyn fmt::Write) -> fmt::Result {
        w.write_str(self)
    }
}

impl Operation for &'static str {
    fn description(&self, w: &mut dyn fmt::Write) -> fmt::Result {
        w.write_str(self)
    }

    fn as_any(&self) -> &dyn Any {
        self
    }
}

///////////////////////////////////////////////////////////////////////////////
// External context

/// A [`Context`] for external operations.
#[derive(Copy, Clone)]
pub struct ExternalContext<O, E> {
    /// Value for [`Context::operation()`].
    pub operation: Option<O>,
    /// Value for [`Context::expected()`].
    pub expected: Option<E>,
}

impl<O, E> Context for ExternalContext<O, E>
where
    O: Operation,
    E: fmt::DisplayBase + Send + Sync + 'static,
{
    fn operation(&self) -> &dyn Operation {
        match &self.operation {
            None => &CoreOperation::Context,
            Some(operation) => operation,
        }
    }

    fn has_expected(&self) -> bool {
        self.expected.is_some()
    }

    fn expected(&self, w: &mut dyn fmt::Write) -> fmt::Result {
        match &self.expected {
            None => Err(fmt::Error),
            Some(expected) => expected.fmt(w),
        }
    }
}

impl<O, E> fmt::Debug for ExternalContext<O, E>
where
    O: fmt::Debug,
    E: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ExternalContext")
            .field("operation", &self.operation)
            .field("expected", &self.expected)
            .finish()
    }
}

///////////////////////////////////////////////////////////////////////////////
// Core context

/// A [`Context`] for core operations supported by `dangerous`.
#[non_exhaustive]
#[derive(Copy, Clone)]
pub struct CoreContext {
    /// The section of input that points to the cause of the error.
    pub span: Span,
    /// Value for [`Context::operation()`].
    pub operation: CoreOperation,
    /// Value for [`Context::expected()`].
    pub expected: CoreExpected,
}

impl CoreContext {
    pub(crate) fn from_operation(operation: CoreOperation, span: Span) -> Self {
        Self {
            span,
            operation,
            expected: CoreExpected::Unknown,
        }
    }

    /// Wraps the context with improved debugging support given the containing
    /// input.
    pub fn debug_for(self, input: MaybeString<'_>) -> DebugFor<'_> {
        DebugFor {
            input,
            context: self,
        }
    }
}

impl Context for CoreContext {
    fn span(&self) -> Option<Span> {
        Some(self.span)
    }

    fn operation(&self) -> &dyn Operation {
        &self.operation
    }

    fn has_expected(&self) -> bool {
        self.expected != CoreExpected::Unknown
    }

    fn expected(&self, w: &mut dyn fmt::Write) -> fmt::Result {
        fmt::DisplayBase::fmt(&self.expected, w)
    }
}

impl fmt::Debug for CoreContext {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("CoreContext")
            .field("span", &self.span)
            .field("operation", &self.operation)
            .field("expected", &self.expected)
            .finish()
    }
}

#[must_use]
pub struct DebugFor<'i> {
    input: MaybeString<'i>,
    context: CoreContext,
}

impl<'i> fmt::Debug for DebugFor<'i> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("CoreContext")
            .field("span", &self.context.span.debug_for(self.input.clone()))
            .field("operation", &self.context.operation)
            .field("expected", &self.context.expected)
            .finish()
    }
}

/// Core operations used by `dangerous`.
#[allow(missing_docs)]
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum CoreOperation {
    // Context
    Context,
    // Entry
    ReadAll,
    ReadPartial,
    // Consuming
    Consume,
    // Skipping
    Skip,
    SkipWhile,
    SkipStrWhile,
    SkipUntil,
    SkipUntilConsume,
    // Taking
    Take,
    TakeUntil,
    TakeUntilConsume,
    TakeWhile,
    TakeConsumed,
    TakeStrWhile,
    TakeRemainingStr,
    // Peeking
    Peek,
    PeekU8,
    PeekChar,
    // Reading
    ReadU8,
    ReadChar,
    ReadArray,
    // Errors
    RecoverIf,
    Verify,
    Expect,
    ExpectExternal,
    // Converting
    IntoNonEmpty,
    IntoExternal,
    IntoString,
}

impl Operation for CoreOperation {
    fn description(&self, w: &mut dyn fmt::Write) -> fmt::Result {
        w.write_str(CoreOperation::description(*self))
    }

    fn as_any(&self) -> &dyn Any {
        self
    }
}

impl CoreOperation {
    fn description(self) -> &'static str {
        match self {
            Self::Context => "<context>",
            Self::ReadAll => "read all input",
            Self::ReadPartial => "read a partial length of input",
            Self::Consume => "consume input",
            Self::Skip => "skip a length of input",
            Self::SkipWhile => "skip input while a pattern matches",
            Self::SkipUntil => "skip input until a pattern matches",
            Self::SkipUntilConsume => "skip input until a pattern matches and consume it",
            Self::SkipStrWhile => "skip UTF-8 input while a condition remains true",
            Self::Take => "take a length of input",
            Self::TakeWhile => "take input while a pattern matches",
            Self::TakeUntil => "take input until a pattern matches",
            Self::TakeUntilConsume => "take input until a pattern matches and consume it",
            Self::TakeConsumed => "take input that was consumed",
            Self::TakeStrWhile => "take UTF-8 input while a condition remains true",
            Self::TakeRemainingStr => "take remaining string within bytes",
            Self::Peek => "peek a length of input",
            Self::PeekU8 => "peek a u8",
            Self::PeekChar => "peek a char",
            Self::ReadU8 => "read a u8",
            Self::ReadChar => "read a char",
            Self::ReadArray => "read an array of bytes",
            Self::RecoverIf => "recover if a condition returns true",
            Self::Verify => "read and verify input",
            Self::Expect => "read and expect a value",
            Self::ExpectExternal => "read and expect an external value",
            Self::IntoNonEmpty => "convert input into non-empty input",
            Self::IntoExternal => "convert input into external type",
            Self::IntoString => "convert input into string",
        }
    }
}

impl fmt::DisplayBase for CoreOperation {
    fn fmt(&self, w: &mut dyn fmt::Write) -> fmt::Result {
        self.description(w)
    }
}

/// Core expectations used by `dangerous`.
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum CoreExpected {
    /// What is expected is unknown.
    ///
    /// This is used to return `false` on [`Context::has_expected()`].
    Unknown,
    /// Non empty input was expected.
    NonEmpty,
    /// An exact value was expected.
    ExactValue,
    /// A pattern match was expected.
    PatternMatch,
    /// No trailing input was expected.
    NoTrailingInput,
    /// Contains the description of the value that was expected.
    Valid(&'static str),
    /// Enough input for a given description of a value was expected.
    EnoughInputFor(&'static str),
}

impl fmt::DisplayBase for CoreExpected {
    fn fmt(&self, w: &mut dyn fmt::Write) -> fmt::Result {
        match *self {
            Self::Unknown => w.write_str("unknown"),
            Self::NonEmpty => w.write_str("non-empty input"),
            Self::ExactValue => w.write_str("exact value"),
            Self::PatternMatch => w.write_str("pattern match"),
            Self::NoTrailingInput => w.write_str("no trailing input"),
            Self::Valid(expected) => w.write_str(expected),
            Self::EnoughInputFor(expected) => {
                w.write_str("enough input for ")?;
                w.write_str(expected)
            }
        }
    }
}

///////////////////////////////////////////////////////////////////////////////

/// Wraps an error making all contexts added to it children of the last
/// operation.
pub struct WithChildContext<E>(E);

impl<E> WithChildContext<E> {
    /// Wrap the error.
    pub fn new(inner: E) -> Self {
        Self(inner)
    }

    /// Unwrap the error from the behaviour.
    pub fn unwrap(self) -> E {
        self.0
    }
}

impl<'i, E> WithContext<'i> for WithChildContext<E>
where
    E: WithContext<'i>,
{
    const PASSTHROUGH: bool = E::PASSTHROUGH;

    #[inline(always)]
    fn with_input(self, _input: impl Input<'i>) -> Self {
        self
    }

    #[inline(always)]
    fn with_context(self, context: impl Context) -> Self {
        Self(self.0.with_context(ChildContext(context)))
    }
}

struct ChildContext<T>(T);

impl<T> Context for ChildContext<T>
where
    T: Context,
{
    fn operation(&self) -> &dyn Operation {
        self.0.operation()
    }

    fn has_expected(&self) -> bool {
        self.0.has_expected()
    }

    fn expected(&self, w: &mut dyn fmt::Write) -> fmt::Result {
        self.0.expected(w)
    }

    fn is_child(&self) -> bool {
        true
    }
}

///////////////////////////////////////////////////////////////////////////////

#[inline(always)]
pub(crate) fn with_context<'i, F, T, E>(
    context: impl Context,
    input: impl Input<'i>,
    f: F,
) -> Result<T, E>
where
    E: WithContext<'i>,
    F: FnOnce() -> Result<T, E>,
{
    match f() {
        Ok(ok) => Ok(ok),
        Err(err) => Err(err.with_context(context).with_input(input)),
    }
}