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}