dcbor_parse/
error.rs

1use logos::Span;
2use thiserror::Error;
3
4use crate::Token;
5
6#[derive(Debug, Error, Clone, PartialEq)]
7#[rustfmt::skip]
8pub enum Error {
9    #[error("Empty input")]
10    EmptyInput,
11    #[error("Unexpected end of input")]
12    UnexpectedEndOfInput,
13    #[error("Extra data at end of input")]
14    ExtraData(Span),
15    #[error("Unexpected token {0:?}")]
16    UnexpectedToken(Box<Token>, Span),
17    #[error("Unrecognized token")]
18    UnrecognizedToken(Span),
19    #[error("Expected comma")]
20    ExpectedComma(Span),
21    #[error("Expected colon")]
22    ExpectedColon(Span),
23    #[error("Unmatched parentheses")]
24    UnmatchedParentheses(Span),
25    #[error("Unmatched braces")]
26    UnmatchedBraces(Span),
27    #[error("Expected map key")]
28    ExpectedMapKey(Span),
29    #[error("Invalid tag value '{0}'")]
30    InvalidTagValue(String, Span),
31    #[error("Unknown tag name '{0}'")]
32    UnknownTagName(String, Span),
33    #[error("Invalid hex string")]
34    InvalidHexString(Span),
35    #[error("Invalid base64 string")]
36    InvalidBase64String(Span),
37    #[error("Unknown UR type '{0}'")]
38    UnknownUrType(String, Span),
39    #[error("Invalid UR '{0}'")]
40    InvalidUr(String, Span),
41    #[error("Invalid known value '{0}'")]
42    InvalidKnownValue(String, Span),
43    #[error("Unknown known value name '{0}'")]
44    UnknownKnownValueName(String, Span),
45    #[error("Invalid date string '{0}'")]
46    InvalidDateString(String, Span),
47    #[error("Duplicate map key")]
48    DuplicateMapKey(Span),
49}
50
51impl Error {
52    pub fn is_default(&self) -> bool {
53        matches!(self, Error::UnrecognizedToken(_))
54    }
55
56    fn format_message(
57        message: &dyn ToString,
58        source: &str,
59        range: &Span,
60    ) -> String {
61        let message = message.to_string();
62        let start = range.start;
63        let end = range.end;
64        // Walk through the bytes up to `start` to find line number and line
65        // start offset
66        let mut line_number = 1;
67        let mut line_start = 0;
68        for (idx, ch) in source.char_indices() {
69            if idx >= start {
70                break;
71            }
72            if ch == '\n' {
73                line_number += 1;
74                line_start = idx + 1;
75            }
76        }
77        // Grab the exact line text (or empty if out of bounds)
78        let line = source.lines().nth(line_number - 1).unwrap_or("");
79        // Column is byte-offset into that line
80        let column = start.saturating_sub(line_start);
81        // Underline at least one caret, even for zero-width spans
82        let underline_len = end.saturating_sub(start).max(1);
83        let caret = " ".repeat(column) + &"^".repeat(underline_len);
84        format!("line {line_number}: {message}\n{line}\n{caret}")
85    }
86
87    #[rustfmt::skip]
88    pub fn full_message(&self, source: &str) -> String {
89        match self {
90            Error::EmptyInput => Self::format_message(self, source, &Span::default()),
91            Error::UnexpectedEndOfInput => Self::format_message(self, source, &(source.len()..source.len())),
92            Error::ExtraData(range) => Self::format_message(self, source, range),
93            Error::UnexpectedToken(_, range) => Self::format_message(self, source, range),
94            Error::UnrecognizedToken(range) => Self::format_message(self, source, range),
95            Error::UnknownUrType(_, range) => Self::format_message(self, source, range),
96            Error::UnmatchedParentheses(range) => Self::format_message(self, source, range),
97            Error::ExpectedComma(range) => Self::format_message(self, source, range),
98            Error::ExpectedColon(range) => Self::format_message(self, source, range),
99            Error::ExpectedMapKey(range) => Self::format_message(self, source, range),
100            Error::UnmatchedBraces(range) => Self::format_message(self, source, range),
101            Error::UnknownTagName(_, range) => Self::format_message(self, source, range),
102            Error::InvalidHexString(range) => Self::format_message(self, source, range),
103            Error::InvalidBase64String(range) => Self::format_message(self, source, range),
104            Error::InvalidTagValue(_, range) => Self::format_message(self, source, range),
105            Error::InvalidUr(_, range) => Self::format_message(self, source, range),
106            Error::InvalidKnownValue(_, range) => Self::format_message(self, source, range),
107            Error::UnknownKnownValueName(_, range) => Self::format_message(self, source, range),
108            Error::InvalidDateString(_, range) => Self::format_message(self, source, range),
109            Error::DuplicateMapKey(range) => Self::format_message(self, source, range),
110        }
111    }
112}
113
114impl Default for Error {
115    fn default() -> Self { Error::UnrecognizedToken(Span::default()) }
116}
117
118pub type Result<T> = std::result::Result<T, Error>;