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#[expect(unused)] use crate::Parser;
17
18#[derive(Debug, PartialEq, Eq, Clone, Copy)]
28pub struct ParsingError<In, Reason = Infallible> {
29 pub rest: In,
31 pub reason: Option<Reason>,
34}
35
36impl<In: Input, Reason: Display> Display for ParsingError<In, Reason> {
37 fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
38 if let Some(reason) = &self.reason {
39 writeln!(f, "{reason}")?;
40 }
41 write!(
42 f,
43 "error source: {}{}",
44 self.rest[..self.rest.len().min(16)].escape_debug(),
45 if self.rest.len() > 16 { "..." } else { "" }
46 )?;
47 Ok(())
48 }
49}
50
51impl<In: Input, Reason: Error> Error for ParsingError<In, Reason> {}
52
53impl<In, Reason> ParsingError<In, Reason> {
54 pub const fn new(rest: In, reason: Reason) -> Self {
56 Self {
57 rest,
58 reason: Some(reason),
59 }
60 }
61
62 pub const fn new_recoverable(rest: In) -> Self {
64 Self { rest, reason: None }
65 }
66
67 pub const fn is_recoverable(&self) -> bool {
69 self.reason.is_none()
70 }
71
72 pub fn reason<NewReason>(self, reason: NewReason) -> ParsingError<In, NewReason> {
74 ParsingError {
75 reason: Some(reason),
76 rest: self.rest,
77 }
78 }
79
80 #[must_use]
82 pub fn or_reason(self, reason: Reason) -> Self {
83 Self {
84 reason: self.reason.or(Some(reason)),
85 rest: self.rest,
86 }
87 }
88
89 #[must_use]
91 pub fn or_reason_if_nonempty(self, reason: Reason) -> Self
92 where
93 In: Input,
94 {
95 Self {
96 reason: self
97 .reason
98 .or_else(|| self.rest.is_empty().not().then_some(reason)),
99 rest: self.rest,
100 }
101 }
102
103 pub fn map_reason<NewReason>(
106 self,
107 f: impl FnOnce(Reason) -> NewReason,
108 ) -> ParsingError<In, NewReason> {
109 ParsingError {
110 reason: self.reason.map(f),
111 rest: self.rest,
112 }
113 }
114
115 #[allow(unreachable_code)]
118 pub fn adapt_reason<NewReason>(self) -> ParsingError<In, NewReason>
119 where
120 Infallible: From<Reason>,
121 {
122 ParsingError {
123 reason: self.reason.map(|x| match Infallible::from(x) {}),
124 rest: self.rest,
125 }
126 }
127
128 pub fn with_src_loc<'a>(
134 self,
135 path: impl PathLike<'a>,
136 src: &'a str,
137 ) -> FullParsingError<'a, Reason>
138 where
139 In: Input,
140 {
141 FullParsingError {
142 loc: Location::find_saturating(self.rest.as_ptr(), src).with_path(path),
143 reason: self.reason,
144 src: src.into(),
145 }
146 }
147
148 #[cfg(feature = "std")]
153 pub fn with_file_loc<'a>(
154 self,
155 path: impl PathLike<'a>,
156 ) -> io::Result<FullParsingError<'a, Reason>>
157 where
158 In: Input,
159 {
160 let path = path.into_path();
161 let src = read_to_string(&path)?;
162 Ok(FullParsingError {
163 loc: Location::find_saturating(self.rest.as_ptr(), &src).with_path(path),
164 reason: self.reason,
165 src: src.into(),
166 })
167 }
168}
169
170#[derive(Debug, Clone)]
176pub struct FullParsingError<'a, Reason> {
177 pub loc: FullLocation<'a>,
179 pub reason: Option<Reason>,
183 pub src: Cow<'a, str>,
185}
186
187impl<Reason: Display> Display for FullParsingError<'_, Reason> {
188 fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
189 if let Some(reason) = &self.reason {
190 writeln!(f, "{reason}")?;
191 }
192 writeln!(f, "--> {}", self.loc)?;
193 let line = self
194 .src
195 .lines()
196 .nth(self.loc.loc.line.get() as usize - 1)
197 .ok_or(core::fmt::Error)?;
198 let line_col_off = self.loc.loc.line.ilog10() as usize + 1;
199 writeln!(f, "{:line_col_off$} |", "")?;
200 writeln!(f, "{:line_col_off$} | {line}", self.loc.loc.line)?;
201 write!(
202 f,
203 "{:line_col_off$} | {:>2$}",
204 "",
205 '^',
206 self.loc.loc.col as usize + 1
207 )?;
208 Ok(())
209 }
210}
211
212impl<Reason: Error> Error for FullParsingError<'_, Reason> {}
213
214impl<Reason> FullParsingError<'_, Reason> {
215 pub fn own(self) -> FullParsingError<'static, Reason> {
217 FullParsingError {
218 loc: self.loc.own(),
219 src: self.src.into_owned().into(),
220 ..self
221 }
222 }
223}
224
225pub type ParsingResult<In, T, Reason = Infallible> =
227 core::result::Result<(In, T), ParsingError<In, Reason>>;