1use std::borrow::{Borrow, Cow};
2use std::error::Error;
3use std::fmt;
4use std::fmt::{Debug, Display};
5
6use nom::error::{ErrorKind, ParseError};
7use nom::{HexDisplay, IResult};
8
9pub(crate) fn make_error<I>(input: I, kind: MshParserErrorKind) -> nom::Err<MshParserError<I>> {
10 MshParserError::from_error_kind(input, kind.clone()).into_nom_error()
11}
12
13pub(crate) fn always_error<I, O>(
15 kind: MshParserErrorKind,
16) -> impl Fn(I) -> IResult<I, O, MshParserError<I>> {
17 move |i: I| Err(make_error(i, kind.clone()))
18}
19
20pub(crate) fn error<I: Clone, F, O>(
22 kind: MshParserErrorKind,
23 f: F,
24) -> impl Fn(I) -> IResult<I, O, MshParserError<I>>
25where
26 F: Fn(I) -> IResult<I, O, MshParserError<I>>,
27{
28 move |i: I| f(i.clone()).with_error(i, kind.clone())
29}
30
31pub(crate) fn context<I: Clone, F, O>(
33 ctx: &'static str,
34 f: F,
35) -> impl Fn(I) -> IResult<I, O, MshParserError<I>>
36where
37 F: Fn(I) -> IResult<I, O, MshParserError<I>>,
38{
39 move |i: I| f(i.clone()).with_context(i, ctx)
40}
41
42pub(crate) fn context_from<I: Clone, C, F, O, S: Clone + Into<Cow<'static, str>>>(
44 ctx: C,
45 f: F,
46) -> impl Fn(I) -> IResult<I, O, MshParserError<I>>
47where
48 C: Fn() -> S,
49 F: Fn(I) -> IResult<I, O, MshParserError<I>>,
50{
51 move |i: I| f(i.clone()).with_context(i, ctx())
52}
53
54#[derive(Copy, Clone, Debug, Eq, PartialEq, thiserror::Error)]
56pub enum ValueType {
57 #[error("unsigned integer")]
59 UnsignedInt,
60 #[error("integer")]
62 Int,
63 #[error("floating point")]
65 Float,
66}
67
68#[rustfmt::skip]
70#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
71pub enum MshParserErrorKind {
72 #[error("MSH file of unsupported format version loaded. Only the MSH file format specification of revision 4.1 is supported.")]
74 UnsupportedMshVersion,
75 #[error("There is no parser available to parse binary {0} values with a size of {1}.")]
77 UnsupportedTypeSize(ValueType, usize),
78 #[error("The MSH file header is not valid.")]
80 InvalidFileHeader,
81 #[error("Unexpected tokens found after file header. Expected a section according to the MSH file format specification.")]
83 InvalidSectionHeader,
84 #[error("An unknown element type was encountered in the MSH file.")]
86 UnknownElement,
87 #[error("There are too many entities to parse them into contiguous memory on the current system (usize type too small).")]
89 TooManyEntities,
90 #[error("A {0} value could not be parsed because it was out of range of the target data type.")]
92 ValueOutOfRange(ValueType),
93 #[error("An invalid entity tag value was encountered, e.g. the internally reserved value of 0, or a max_tag that is smaller than a min_tag")]
95 InvalidTag,
96 #[error("An invalid parameter value was encountered.")]
98 InvalidParameter,
99 #[error("An invalid element definition was encountered.")]
101 InvalidElementDefinition,
102 #[error("An invalid node definition was encountered.")]
104 InvalidNodeDefinition,
105 #[error("An unimplemented feature was detected.")]
107 Unimplemented,
108 #[error("{0}")]
110 Context(Cow<'static,str>),
111 #[error("{0:?}")]
113 NomError(ErrorKind),
114}
115
116impl MshParserErrorKind {
117 pub(crate) fn into_error<I>(self, input: I) -> MshParserError<I> {
118 MshParserError::from_error_kind(input, self)
119 }
120
121 pub fn is_nom_error(&self) -> bool {
123 match self {
124 MshParserErrorKind::NomError(_) => true,
125 _ => false,
126 }
127 }
128
129 pub fn context(&self) -> Option<&str> {
131 match self {
132 MshParserErrorKind::Context(ctx) => Some(ctx.borrow()),
133 _ => None,
134 }
135 }
136}
137
138impl From<ErrorKind> for MshParserErrorKind {
139 fn from(ek: ErrorKind) -> Self {
140 MshParserErrorKind::NomError(ek)
141 }
142}
143
144pub struct MshParserError<I> {
146 pub backtrace: Vec<(I, MshParserErrorKind)>,
148}
149
150impl<I> MshParserError<I> {
151 fn new() -> Self {
153 Self {
154 backtrace: Vec::new(),
155 }
156 }
157
158 pub(crate) fn from_error_kind(input: I, kind: MshParserErrorKind) -> Self {
160 Self {
161 backtrace: vec![(input, kind)],
162 }
163 }
164
165 pub(crate) fn into_nom_error(self) -> nom::Err<Self> {
167 nom::Err::Error(self)
168 }
169
170 pub(crate) fn into_nom_failure(self) -> nom::Err<Self> {
172 nom::Err::Failure(self)
173 }
174
175 pub(crate) fn with_append(mut self, input: I, kind: MshParserErrorKind) -> Self {
177 self.backtrace.push((input, kind));
178 self
179 }
180
181 pub(crate) fn with_context<S: Into<Cow<'static, str>>>(self, input: I, ctx: S) -> Self {
183 self.with_append(input, MshParserErrorKind::Context(ctx.into()))
184 }
185
186 pub fn begin_msh_errors(&self) -> impl Iterator<Item = &(I, MshParserErrorKind)> {
188 self.backtrace.iter().skip_while(|(_, e)| e.is_nom_error())
189 }
190
191 pub fn filter_msh_errors(&self) -> impl Iterator<Item = &(I, MshParserErrorKind)> {
193 self.backtrace.iter().filter(|(_, e)| !e.is_nom_error())
194 }
195
196 pub fn first_msh_error(&self) -> Option<MshParserErrorKind> {
198 self.begin_msh_errors().next().map(|(_, ek)| ek).cloned()
199 }
200}
201
202impl<I: Clone> MshParserError<I> {
203 pub fn filtered_backtrace(&self) -> Vec<(I, MshParserErrorKind)> {
205 self.filter_msh_errors().cloned().collect()
206 }
207}
208
209impl<I: Debug> Debug for MshParserError<I> {
210 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
211 write!(f, "MshParserError({:?})", self.backtrace)
212 }
213}
214
215impl<I: Debug + HexDisplay + ?Sized> Display for MshParserError<&I> {
216 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
219 let backtrace = self.filtered_backtrace();
221 if backtrace.len() > 1 {
222 write!(f, "During parsing...\n")?;
223 for (_, ek) in backtrace[1..].iter().rev() {
224 if let Some(c) = ek.context() {
225 write!(f, "\tin {},\n", c)?;
226 } else {
227 write!(f, "\tin {},\n", ek)?;
228 }
229 }
230 write!(f, "an error occurred: ")?;
231 write!(f, "{}\n", backtrace[0].1)?;
232 write!(
233 f,
234 "Hex dump of the file at the error location:\n{}",
235 backtrace[0].0.to_hex(16)
237 )?;
238 Ok(())
239 } else if backtrace.len() == 1 {
240 write!(f, "An error occurred during: ")?;
241 write!(f, "{}", backtrace[0].1)?;
242 Ok(())
243 } else {
244 write!(f, "Unknown error occurred\n")
245 }
246 }
247}
248
249impl<I> ParseError<I> for MshParserError<I> {
250 fn from_error_kind(input: I, kind: ErrorKind) -> Self {
251 Self {
252 backtrace: vec![(input, MshParserErrorKind::NomError(kind))],
253 }
254 }
255
256 fn append(input: I, kind: ErrorKind, mut other: Self) -> Self {
257 other
258 .backtrace
259 .push((input, MshParserErrorKind::NomError(kind)));
260 other
261 }
262
263 fn add_context(input: I, ctx: &'static str, mut other: Self) -> Self {
264 other
265 .backtrace
266 .push((input, MshParserErrorKind::Context(Cow::Borrowed(ctx))));
267 other
268 }
269}
270
271impl<I: Debug + HexDisplay + ?Sized> Error for MshParserError<&I> {}
272
273impl<I: Debug, E: Into<MshParserError<I>>> From<nom::Err<E>> for MshParserError<I> {
275 fn from(error: nom::Err<E>) -> Self {
276 match error {
277 nom::Err::Error(ve) | nom::Err::Failure(ve) => ve.into(),
278 _ => Self::new(),
279 }
280 }
281}
282
283pub(crate) trait MapMshError<I> {
284 fn map_msh_err<F>(self, f: F) -> Self
286 where
287 F: FnOnce(MshParserError<I>) -> MshParserError<I>;
288
289 fn with_error(self, input: I, kind: MshParserErrorKind) -> Self
291 where
292 Self: Sized,
293 {
294 self.map_msh_err(|e| e.with_append(input, kind))
295 }
296
297 fn with_context<S: Into<Cow<'static, str>>>(self, input: I, ctx: S) -> Self
299 where
300 Self: Sized,
301 {
302 self.map_msh_err(|e| e.with_context(input, ctx))
303 }
304
305 fn with_context_from<S: Into<Cow<'static, str>>, C: Fn() -> S>(self, input: I, ctx: C) -> Self
307 where
308 Self: Sized,
309 {
310 self.map_msh_err(|e| e.with_context(input, ctx()))
311 }
312}
313
314impl<I> MapMshError<I> for nom::Err<MshParserError<I>> {
316 fn map_msh_err<F>(self, f: F) -> Self
317 where
318 F: FnOnce(MshParserError<I>) -> MshParserError<I>,
319 {
320 self.map(f)
321 }
322}
323
324impl<I, O> MapMshError<I> for IResult<I, O, MshParserError<I>> {
326 fn map_msh_err<F>(self, f: F) -> Self
327 where
328 F: FnOnce(MshParserError<I>) -> MshParserError<I>,
329 {
330 self.map_err(|err| err.map(f))
331 }
332}