Skip to main content

cddl_cat/parser/
parse_err.rs

1//! Parser error types and related utilities
2//!
3
4use std::borrow::Cow;
5
6use nom::error::FromExternalError;
7use thiserror::Error;
8
9/// The "kind" of error generated during CDDL parsing.
10#[non_exhaustive]
11#[derive(Debug, PartialEq, Eq)]
12pub enum ErrorKind {
13    /// An integer didn't parse correctly.
14    MalformedInteger,
15    /// A floating-point number didn't parse correctly.
16    MalformedFloat,
17    /// A hex literal didn't parse correctly.
18    MalformedHex,
19    /// A malformed text string
20    MalformedText,
21    /// A malformed base64 byte string
22    MalformedBase64,
23    /// A nonspecific parsing error.
24    Unparseable,
25}
26
27/// An error that occurred during CDDL parsing.
28#[derive(Debug, Error)]
29// thiserror will generate a Display implementation.
30#[error("{kind:?}({ctx})")]
31pub struct ParseError {
32    /// The "kind" of error generated during CDDL parsing.
33    pub kind: ErrorKind,
34    /// A snippet of text from the CDDL input that may be the cause of the error.
35    pub ctx: String,
36}
37
38// Convert a temporary error into an owned 'static error.
39impl From<CowParseError<'_>> for ParseError {
40    fn from(err: CowParseError<'_>) -> Self {
41        ParseError {
42            kind: err.kind,
43            // Create an owned String from the Cow<'_, str>
44            ctx: err.ctx.into(),
45        }
46    }
47}
48
49#[derive(Debug, PartialEq)]
50pub(crate) struct CowParseError<'a> {
51    /// The "kind" of error generated during CDDL parsing.
52    pub kind: ErrorKind,
53    /// A snippet of text from the CDDL input that may be the cause of the error.
54    ///
55    /// This may contain either a borrowed 'str or an owned String. This is useful
56    /// because many transient errors are generated during parsing and thrown away,
57    /// and there's no point allocating memory for them until we are done parsing.
58    pub ctx: Cow<'a, str>,
59}
60
61// Convert a bounded lifetime ParseError into a ParseError<'static>.
62
63impl<I, E> FromExternalError<I, E> for CowParseError<'_> {
64    fn from_external_error(_input: I, _kind: nom::error::ErrorKind, _e: E) -> Self {
65        CowParseError {
66            kind: ErrorKind::Unparseable,
67            ctx: "nom-error".into(),
68        }
69    }
70}
71
72pub(crate) fn parse_error<'a, S: Into<Cow<'a, str>>>(kind: ErrorKind, ctx: S) -> CowParseError<'a> {
73    CowParseError {
74        kind,
75        ctx: ctx.into(),
76    }
77}
78
79// Used when calling all_consuming() at the end of the parsing process.
80impl From<nom::Err<CowParseError<'_>>> for ParseError {
81    fn from(e: nom::Err<CowParseError>) -> ParseError {
82        match e {
83            nom::Err::Incomplete(_) => parse_error(ErrorKind::Unparseable, "Incomplete"),
84            nom::Err::Error(pe) => pe,
85            nom::Err::Failure(pe) => pe,
86        }
87        .into()
88    }
89}
90
91// FIXME: the name collision here makes the code hard to read
92impl<'a, I: Into<Cow<'a, str>>> nom::error::ParseError<I> for CowParseError<'a> {
93    fn from_error_kind(input: I, _kind: nom::error::ErrorKind) -> Self {
94        parse_error(ErrorKind::Unparseable, input)
95    }
96
97    fn append(_input: I, _kind: nom::error::ErrorKind, other: Self) -> Self {
98        // FIXME: It's not obvious what I should do here, or
99        // when a proper implementation will be necessary...
100        other
101    }
102}