1pub type Result<T, E = Error> = core::result::Result<T, E>;
2
3#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
5#[non_exhaustive]
6pub struct Mark {
7 pub index: u64,
9 pub line: u64,
11 pub column: u64,
13}
14
15impl std::fmt::Display for Mark {
16 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17 write!(f, "line {} column {}", self.line + 1, self.column + 1)
18 }
19}
20
21#[derive(Debug)]
22#[allow(clippy::struct_field_names)]
23struct Problem {
24 pub problem: &'static str,
25 pub problem_mark: Mark,
26 pub context: &'static str,
27 pub context_mark: Mark,
28}
29
30#[derive(Debug)]
31enum ErrorImpl {
32 Reader {
33 problem: &'static str,
34 offset: usize,
35 value: i32,
36 },
37 Scanner(Problem),
38 Parser(Problem),
39 Composer(Problem),
40 Emitter(&'static str),
41 Io(std::io::Error),
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub enum ErrorKind {
46 Reader,
47 Scanner,
48 Parser,
49 Composer,
50 Emitter,
51 Io,
52}
53
54#[derive(Debug)]
55pub struct Error(Box<ErrorImpl>);
56
57impl From<std::io::Error> for Error {
58 fn from(value: std::io::Error) -> Self {
59 Self(Box::new(ErrorImpl::Io(value)))
60 }
61}
62
63impl Error {
64 pub(crate) fn reader(problem: &'static str, offset: usize, value: i32) -> Self {
65 Self(Box::new(ErrorImpl::Reader {
66 problem,
67 offset,
68 value,
69 }))
70 }
71
72 pub(crate) fn scanner(
73 context: &'static str,
74 context_mark: Mark,
75 problem: &'static str,
76 problem_mark: Mark,
77 ) -> Self {
78 Self(Box::new(ErrorImpl::Scanner(Problem {
79 problem,
80 problem_mark,
81 context,
82 context_mark,
83 })))
84 }
85
86 pub(crate) fn parser(
87 context: &'static str,
88 context_mark: Mark,
89 problem: &'static str,
90 problem_mark: Mark,
91 ) -> Self {
92 Self(Box::new(ErrorImpl::Parser(Problem {
93 problem,
94 problem_mark,
95 context,
96 context_mark,
97 })))
98 }
99
100 pub(crate) fn composer(
101 context: &'static str,
102 context_mark: Mark,
103 problem: &'static str,
104 problem_mark: Mark,
105 ) -> Self {
106 Self(Box::new(ErrorImpl::Composer(Problem {
107 problem,
108 problem_mark,
109 context,
110 context_mark,
111 })))
112 }
113
114 pub(crate) fn emitter(problem: &'static str) -> Self {
115 Self(Box::new(ErrorImpl::Emitter(problem)))
116 }
117
118 pub fn kind(&self) -> ErrorKind {
119 match &*self.0 {
120 ErrorImpl::Reader { .. } => ErrorKind::Reader,
121 ErrorImpl::Scanner(_) => ErrorKind::Scanner,
122 ErrorImpl::Parser(_) => ErrorKind::Parser,
123 ErrorImpl::Composer(_) => ErrorKind::Composer,
124 ErrorImpl::Emitter(_) => ErrorKind::Emitter,
125 ErrorImpl::Io(_) => ErrorKind::Io,
126 }
127 }
128
129 pub fn problem_mark(&self) -> Option<Mark> {
130 match &*self.0 {
131 ErrorImpl::Reader { .. } | ErrorImpl::Emitter(_) | ErrorImpl::Io(_) => None,
132 ErrorImpl::Scanner(p) | ErrorImpl::Parser(p) | ErrorImpl::Composer(p) => {
133 Some(p.problem_mark)
134 }
135 }
136 }
137
138 pub fn context_mark(&self) -> Option<Mark> {
139 match &*self.0 {
140 ErrorImpl::Reader { .. } | ErrorImpl::Emitter(..) | ErrorImpl::Io(_) => None,
141 ErrorImpl::Scanner(p) | ErrorImpl::Parser(p) | ErrorImpl::Composer(p) => {
142 if p.context.is_empty() {
143 None
144 } else {
145 Some(p.context_mark)
146 }
147 }
148 }
149 }
150
151 pub fn problem(&self) -> &'static str {
152 match &*self.0 {
153 ErrorImpl::Reader { problem, .. } | ErrorImpl::Emitter(problem) => problem,
154 ErrorImpl::Scanner(p) | ErrorImpl::Parser(p) | ErrorImpl::Composer(p) => p.problem,
155 ErrorImpl::Io(_) => "I/O error",
156 }
157 }
158
159 pub fn context(&self) -> Option<&'static str> {
160 match &*self.0 {
161 ErrorImpl::Reader { .. } | ErrorImpl::Emitter(..) | ErrorImpl::Io(_) => None,
162 ErrorImpl::Scanner(p) | ErrorImpl::Parser(p) | ErrorImpl::Composer(p) => {
163 if p.context.is_empty() {
164 None
165 } else {
166 Some(p.context)
167 }
168 }
169 }
170 }
171}
172
173impl std::error::Error for Error {
174 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
175 if let ErrorImpl::Io(err) = &*self.0 {
176 Some(err)
177 } else {
178 None
179 }
180 }
181}
182
183impl TryFrom<Error> for std::io::Error {
184 type Error = Error;
185
186 fn try_from(value: Error) -> Result<Self, Self::Error> {
187 if value.kind() == ErrorKind::Io {
188 if let ErrorImpl::Io(err) = *value.0 {
189 Ok(err)
190 } else {
191 unreachable!()
192 }
193 } else {
194 Err(value)
195 }
196 }
197}
198
199impl core::fmt::Display for ErrorKind {
200 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201 f.write_str(match self {
202 ErrorKind::Reader => "Reader",
203 ErrorKind::Scanner => "Scanner",
204 ErrorKind::Parser => "Parser",
205 ErrorKind::Composer => "Composer",
206 ErrorKind::Emitter => "Emitter",
207 ErrorKind::Io => "I/O",
208 })
209 }
210}
211
212impl core::fmt::Display for Problem {
213 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
214 let Self {
215 problem,
216 problem_mark,
217 context,
218 context_mark,
219 } = self;
220
221 if self.context.is_empty() {
222 write!(f, "{problem_mark}: {problem}")
223 } else {
224 write!(f, "{problem_mark}: {problem} {context} ({context_mark})")
225 }
226 }
227}
228
229impl core::fmt::Display for Error {
230 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
231 write!(f, "{} error: ", self.kind())?;
232 match *self.0 {
233 ErrorImpl::Reader {
234 problem,
235 offset,
236 value,
237 } => write!(f, "{problem} (offset {offset}, value {value})"),
238 ErrorImpl::Scanner(ref p) | ErrorImpl::Parser(ref p) | ErrorImpl::Composer(ref p) => {
239 write!(f, "{p}")
240 }
241 ErrorImpl::Emitter(problem) => write!(f, "{problem}"),
242 ErrorImpl::Io(ref err) => write!(f, "{err}"),
243 }
244 }
245}