1extern crate alloc;
2
3use {
4 crate::{utils::PathLike, FullLocation, Input, Location},
5 alloc::borrow::Cow,
6 core::{
7 convert::Infallible,
8 error::Error,
9 fmt::{Debug, Display, Formatter},
10 ops::Not,
11 },
12 std::{fs::read_to_string, io},
13};
14
15#[derive(Debug, PartialEq, Eq, Clone, Copy)]
25pub struct ParsingError<In, Reason = Infallible> {
26 pub rest: In,
28 pub reason: Option<Reason>,
31}
32
33impl<In: Input, Reason: Display> Display for ParsingError<In, Reason> {
34 fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
35 if let Some(reason) = &self.reason {
36 writeln!(f, "{reason}")?;
37 }
38 write!(
39 f,
40 "error source: {}{}",
41 self.rest[..self.rest.len().min(16)].escape_debug(),
42 if self.rest.len() > 16 { "..." } else { "" }
43 )?;
44 Ok(())
45 }
46}
47
48impl<In: Input, Reason: Error> Error for ParsingError<In, Reason> {}
49
50impl<In, Reason> ParsingError<In, Reason> {
51 pub const fn new(rest: In, reason: Reason) -> Self {
53 Self {
54 rest,
55 reason: Some(reason),
56 }
57 }
58
59 pub const fn new_recoverable(rest: In) -> Self {
61 Self { rest, reason: None }
62 }
63
64 pub const fn is_recoverable(&self) -> bool {
66 self.reason.is_none()
67 }
68
69 pub fn reason<NewReason>(self, reason: NewReason) -> ParsingError<In, NewReason> {
71 ParsingError {
72 reason: Some(reason),
73 rest: self.rest,
74 }
75 }
76
77 #[must_use]
79 pub fn or_reason(self, reason: Reason) -> Self {
80 Self {
81 reason: self.reason.or(Some(reason)),
82 rest: self.rest,
83 }
84 }
85
86 #[must_use]
88 pub fn or_reason_if_nonempty(self, reason: Reason) -> Self
89 where
90 In: Input,
91 {
92 Self {
93 reason: self
94 .reason
95 .or_else(|| self.rest.is_empty().not().then_some(reason)),
96 rest: self.rest,
97 }
98 }
99
100 pub fn map_reason<NewReason>(
103 self,
104 f: impl FnOnce(Reason) -> NewReason,
105 ) -> ParsingError<In, NewReason> {
106 ParsingError {
107 reason: self.reason.map(f),
108 rest: self.rest,
109 }
110 }
111
112 #[allow(unreachable_code)]
115 pub fn adapt_reason<NewReason>(self) -> ParsingError<In, NewReason>
116 where
117 Infallible: From<Reason>,
118 {
119 ParsingError {
120 reason: self.reason.map(|x| match Infallible::from(x) {}),
121 rest: self.rest,
122 }
123 }
124
125 pub fn with_src_loc<'a>(
131 self,
132 path: impl PathLike<'a>,
133 src: &'a str,
134 ) -> FullParsingError<'a, Reason>
135 where
136 In: Input,
137 {
138 FullParsingError {
139 loc: Location::find_saturating(self.rest.as_ptr(), src).with_path(path),
140 reason: self.reason,
141 src: src.into(),
142 }
143 }
144
145 #[cfg(feature = "std")]
150 pub fn with_file_loc<'a>(
151 self,
152 path: impl PathLike<'a>,
153 ) -> io::Result<FullParsingError<'a, Reason>>
154 where
155 In: Input,
156 {
157 let path = path.into_path();
158 let src = read_to_string(&path)?;
159 Ok(FullParsingError {
160 loc: Location::find_saturating(self.rest.as_ptr(), &src).with_path(path),
161 reason: self.reason,
162 src: src.into(),
163 })
164 }
165}
166
167#[derive(Debug, Clone)]
177pub struct FullParsingError<'a, Reason> {
178 pub loc: FullLocation<'a>,
180 pub reason: Option<Reason>,
184 pub src: Cow<'a, str>,
186}
187
188impl<Reason: Display> Display for FullParsingError<'_, Reason> {
189 fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
190 if let Some(reason) = &self.reason {
191 writeln!(f, "{reason}")?;
192 }
193 writeln!(f, "--> {}", self.loc)?;
194 let line = self
195 .src
196 .lines()
197 .nth(self.loc.loc.line.get() as usize - 1)
198 .ok_or(core::fmt::Error)?;
199 let line_col_off = self.loc.loc.line.ilog10() as usize + 1;
200 writeln!(f, "{:line_col_off$} |", "")?;
201 writeln!(f, "{:line_col_off$} | {line}", self.loc.loc.line)?;
202 write!(
203 f,
204 "{:line_col_off$} | {:>2$}",
205 "",
206 '^',
207 self.loc.loc.col as usize + 1
208 )?;
209 Ok(())
210 }
211}
212
213impl<Reason: Error> Error for FullParsingError<'_, Reason> {}
214
215impl<Reason> FullParsingError<'_, Reason> {
216 pub fn own(self) -> FullParsingError<'static, Reason> {
218 FullParsingError {
219 loc: self.loc.own(),
220 src: self.src.into_owned().into(),
221 ..self
222 }
223 }
224}
225
226pub type ParsingResult<In, T, Reason = Infallible> =
228 core::result::Result<(In, T), ParsingError<In, Reason>>;