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}
46
47impl Error {
48 pub fn is_default(&self) -> bool {
49 matches!(self, Error::UnrecognizedToken(_))
50 }
51
52 fn format_message(
53 message: &dyn ToString,
54 source: &str,
55 range: &Span,
56 ) -> String {
57 let message = message.to_string();
58 let start = range.start;
59 let end = range.end;
60 let mut line_number = 1;
63 let mut line_start = 0;
64 for (idx, ch) in source.char_indices() {
65 if idx >= start {
66 break;
67 }
68 if ch == '\n' {
69 line_number += 1;
70 line_start = idx + 1;
71 }
72 }
73 let line = source.lines().nth(line_number - 1).unwrap_or("");
75 let column = start.saturating_sub(line_start);
77 let underline_len = end.saturating_sub(start).max(1);
79 let caret = " ".repeat(column) + &"^".repeat(underline_len);
80 format!("line {line_number}: {message}\n{line}\n{caret}")
81 }
82
83 #[rustfmt::skip]
84 pub fn full_message(&self, source: &str) -> String {
85 match self {
86 Error::EmptyInput => Self::format_message(self, source, &Span::default()),
87 Error::UnexpectedEndOfInput => Self::format_message(self, source, &(source.len()..source.len())),
88 Error::ExtraData(range) => Self::format_message(self, source, range),
89 Error::UnexpectedToken(_, range) => Self::format_message(self, source, range),
90 Error::UnrecognizedToken(range) => Self::format_message(self, source, range),
91 Error::UnknownUrType(_, range) => Self::format_message(self, source, range),
92 Error::UnmatchedParentheses(range) => Self::format_message(self, source, range),
93 Error::ExpectedComma(range) => Self::format_message(self, source, range),
94 Error::ExpectedColon(range) => Self::format_message(self, source, range),
95 Error::ExpectedMapKey(range) => Self::format_message(self, source, range),
96 Error::UnmatchedBraces(range) => Self::format_message(self, source, range),
97 Error::UnknownTagName(_, range) => Self::format_message(self, source, range),
98 Error::InvalidHexString(range) => Self::format_message(self, source, range),
99 Error::InvalidBase64String(range) => Self::format_message(self, source, range),
100 Error::InvalidTagValue(_, range) => Self::format_message(self, source, range),
101 Error::InvalidUr(_, range) => Self::format_message(self, source, range),
102 Error::InvalidKnownValue(_, range) => Self::format_message(self, source, range),
103 Error::UnknownKnownValueName(_, range) => Self::format_message(self, source, range),
104 }
105 }
106}
107
108impl Default for Error {
109 fn default() -> Self { Error::UnrecognizedToken(Span::default()) }
110}
111
112pub type Result<T> = std::result::Result<T, Error>;