1use crate::Span;
4use std::fmt;
5
6#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct ParseError {
9 pub kind: ParseErrorKind,
11 pub span: Span,
13 pub context: Option<String>,
15}
16
17impl ParseError {
18 #[must_use]
20 pub const fn new(kind: ParseErrorKind, span: Span) -> Self {
21 Self {
22 kind,
23 span,
24 context: None,
25 }
26 }
27
28 #[must_use]
30 pub fn with_context(mut self, context: impl Into<String>) -> Self {
31 self.context = Some(context.into());
32 self
33 }
34
35 #[must_use]
37 pub const fn span(&self) -> (usize, usize) {
38 (self.span.start, self.span.end)
39 }
40
41 #[must_use]
43 pub const fn kind_code(&self) -> u32 {
44 match &self.kind {
45 ParseErrorKind::UnexpectedChar(_) => 1,
46 ParseErrorKind::UnexpectedEof => 2,
47 ParseErrorKind::Expected(_) => 3,
48 ParseErrorKind::InvalidDate(_) => 4,
49 ParseErrorKind::InvalidNumber(_) => 5,
50 ParseErrorKind::InvalidAccount(_) => 6,
51 ParseErrorKind::InvalidCurrency(_) => 7,
52 ParseErrorKind::UnclosedString => 8,
53 ParseErrorKind::InvalidEscape(_) => 9,
54 ParseErrorKind::MissingField(_) => 10,
55 ParseErrorKind::IndentationError => 11,
56 ParseErrorKind::SyntaxError(_) => 12,
57 ParseErrorKind::MissingNewline => 13,
58 }
59 }
60
61 #[must_use]
63 pub fn message(&self) -> String {
64 format!("{}", self.kind)
65 }
66
67 #[must_use]
69 pub const fn label(&self) -> &str {
70 match &self.kind {
71 ParseErrorKind::UnexpectedChar(_) => "unexpected character",
72 ParseErrorKind::UnexpectedEof => "unexpected end of file",
73 ParseErrorKind::Expected(_) => "expected different token",
74 ParseErrorKind::InvalidDate(_) => "invalid date",
75 ParseErrorKind::InvalidNumber(_) => "invalid number",
76 ParseErrorKind::InvalidAccount(_) => "invalid account",
77 ParseErrorKind::InvalidCurrency(_) => "invalid currency",
78 ParseErrorKind::UnclosedString => "unclosed string",
79 ParseErrorKind::InvalidEscape(_) => "invalid escape",
80 ParseErrorKind::MissingField(_) => "missing field",
81 ParseErrorKind::IndentationError => "indentation error",
82 ParseErrorKind::SyntaxError(_) => "parse error",
83 ParseErrorKind::MissingNewline => "syntax error",
84 }
85 }
86}
87
88impl fmt::Display for ParseError {
89 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90 write!(f, "{}", self.kind)?;
91 if let Some(ctx) = &self.context {
92 write!(f, " ({ctx})")?;
93 }
94 Ok(())
95 }
96}
97
98impl std::error::Error for ParseError {}
99
100#[derive(Debug, Clone, PartialEq, Eq)]
102pub enum ParseErrorKind {
103 UnexpectedChar(char),
105 UnexpectedEof,
107 Expected(String),
109 InvalidDate(String),
111 InvalidNumber(String),
113 InvalidAccount(String),
115 InvalidCurrency(String),
117 UnclosedString,
119 InvalidEscape(char),
121 MissingField(String),
123 IndentationError,
125 SyntaxError(String),
127 MissingNewline,
129}
130
131impl fmt::Display for ParseErrorKind {
132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 match self {
134 Self::UnexpectedChar(c) => write!(f, "syntax error: unexpected '{c}'"),
135 Self::UnexpectedEof => write!(f, "unexpected end of file"),
136 Self::Expected(what) => write!(f, "expected {what}"),
137 Self::InvalidDate(s) => write!(f, "invalid date '{s}'"),
138 Self::InvalidNumber(s) => write!(f, "invalid number '{s}'"),
139 Self::InvalidAccount(s) => write!(f, "invalid account '{s}'"),
140 Self::InvalidCurrency(s) => write!(f, "invalid currency '{s}'"),
141 Self::UnclosedString => write!(f, "unclosed string literal"),
142 Self::InvalidEscape(c) => write!(f, "invalid escape sequence '\\{c}'"),
143 Self::MissingField(field) => write!(f, "missing required field: {field}"),
144 Self::IndentationError => write!(f, "indentation error"),
145 Self::SyntaxError(msg) => write!(f, "parse error: {msg}"),
146 Self::MissingNewline => write!(f, "syntax error: missing final newline"),
147 }
148 }
149}