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 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 let line = source.lines().nth(line_number - 1).unwrap_or("");
79 let column = start.saturating_sub(line_start);
81 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>;