1use std::fmt;
2
3use nom::{
4 error::{ContextError, ErrorKind, FromExternalError, ParseError},
5 ErrorConvert,
6};
7
8#[derive(Clone, Debug, Eq, PartialEq)]
12pub struct VerboseError<I> {
13 pub errors: Vec<(I, VerboseErrorKind)>,
16}
17
18#[derive(Clone, Debug, Eq, PartialEq)]
19pub enum VerboseErrorKind {
21 Context(&'static str),
23 Char(char),
25 Nom(ErrorKind),
27}
28
29impl<I> ParseError<I> for VerboseError<I> {
30 fn from_error_kind(input: I, kind: ErrorKind) -> Self {
31 VerboseError {
32 errors: vec![(input, VerboseErrorKind::Nom(kind))],
33 }
34 }
35
36 fn append(input: I, kind: ErrorKind, mut other: Self) -> Self {
37 other.errors.push((input, VerboseErrorKind::Nom(kind)));
38 other
39 }
40
41 fn from_char(input: I, c: char) -> Self {
42 VerboseError {
43 errors: vec![(input, VerboseErrorKind::Char(c))],
44 }
45 }
46}
47
48impl<I> ContextError<I> for VerboseError<I> {
49 fn add_context(input: I, ctx: &'static str, mut other: Self) -> Self {
50 other.errors.push((input, VerboseErrorKind::Context(ctx)));
51 other
52 }
53}
54
55impl<I, E> FromExternalError<I, E> for VerboseError<I> {
56 fn from_external_error(input: I, kind: ErrorKind, _e: E) -> Self {
58 Self::from_error_kind(input, kind)
59 }
60}
61
62impl<I: fmt::Display> fmt::Display for VerboseError<I> {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 writeln!(f, "Parse error:")?;
65 for (input, error) in &self.errors {
66 match error {
67 VerboseErrorKind::Nom(e) => writeln!(f, "{:?} at: {}", e, input)?,
68 VerboseErrorKind::Char(c) => writeln!(f, "expected '{}' at: {}", c, input)?,
69 VerboseErrorKind::Context(s) => writeln!(f, "in section '{}', at: {}", s, input)?,
70 }
71 }
72
73 Ok(())
74 }
75}
76
77impl<I: fmt::Debug + fmt::Display> std::error::Error for VerboseError<I> {}
78
79impl From<VerboseError<&[u8]>> for VerboseError<Vec<u8>> {
80 fn from(value: VerboseError<&[u8]>) -> Self {
81 VerboseError {
82 errors: value
83 .errors
84 .into_iter()
85 .map(|(i, e)| (i.to_owned(), e))
86 .collect(),
87 }
88 }
89}
90
91impl From<VerboseError<&str>> for VerboseError<String> {
92 fn from(value: VerboseError<&str>) -> Self {
93 VerboseError {
94 errors: value
95 .errors
96 .into_iter()
97 .map(|(i, e)| (i.to_owned(), e))
98 .collect(),
99 }
100 }
101}
102
103impl<I> ErrorConvert<VerboseError<I>> for VerboseError<(I, usize)> {
104 fn convert(self) -> VerboseError<I> {
105 VerboseError {
106 errors: self.errors.into_iter().map(|(i, e)| (i.0, e)).collect(),
107 }
108 }
109}
110
111impl<I> ErrorConvert<VerboseError<(I, usize)>> for VerboseError<I> {
112 fn convert(self) -> VerboseError<(I, usize)> {
113 VerboseError {
114 errors: self.errors.into_iter().map(|(i, e)| ((i, 0), e)).collect(),
115 }
116 }
117}
118
119pub fn convert_error<I: core::ops::Deref<Target = str>>(input: I, e: VerboseError<I>) -> String {
124 use nom::Offset;
125 use std::fmt::Write;
126
127 let mut result = String::new();
128
129 for (i, (substring, kind)) in e.errors.iter().enumerate() {
130 let offset = input.offset(substring);
131
132 if input.is_empty() {
133 match kind {
134 VerboseErrorKind::Char(c) => {
135 write!(&mut result, "{}: expected '{}', got empty input\n\n", i, c)
136 }
137 VerboseErrorKind::Context(s) => write!(&mut result, "{}: in {}, got empty input\n\n", i, s),
138 VerboseErrorKind::Nom(e) => write!(&mut result, "{}: in {:?}, got empty input\n\n", i, e),
139 }
140 } else {
141 let prefix = &input.as_bytes()[..offset];
142
143 let line_number = prefix.iter().filter(|&&b| b == b'\n').count() + 1;
145
146 let line_begin = prefix
149 .iter()
150 .rev()
151 .position(|&b| b == b'\n')
152 .map(|pos| offset - pos)
153 .unwrap_or(0);
154
155 let line = input[line_begin..]
157 .lines()
158 .next()
159 .unwrap_or(&input[line_begin..])
160 .trim_end();
161
162 let column_number = line.offset(substring) + 1;
164
165 match kind {
166 VerboseErrorKind::Char(c) => {
167 if let Some(actual) = substring.chars().next() {
168 write!(
169 &mut result,
170 "{i}: at line {line_number}:\n\
171 {line}\n\
172 {caret:>column$}\n\
173 expected '{expected}', found {actual}\n\n",
174 i = i,
175 line_number = line_number,
176 line = line,
177 caret = '^',
178 column = column_number,
179 expected = c,
180 actual = actual,
181 )
182 } else {
183 write!(
184 &mut result,
185 "{i}: at line {line_number}:\n\
186 {line}\n\
187 {caret:>column$}\n\
188 expected '{expected}', got end of input\n\n",
189 i = i,
190 line_number = line_number,
191 line = line,
192 caret = '^',
193 column = column_number,
194 expected = c,
195 )
196 }
197 }
198 VerboseErrorKind::Context(s) => write!(
199 &mut result,
200 "{i}: at line {line_number}, in {context}:\n\
201 {line}\n\
202 {caret:>column$}\n\n",
203 i = i,
204 line_number = line_number,
205 context = s,
206 line = line,
207 caret = '^',
208 column = column_number,
209 ),
210 VerboseErrorKind::Nom(e) => write!(
211 &mut result,
212 "{i}: at line {line_number}, in {nom_err:?}:\n\
213 {line}\n\
214 {caret:>column$}\n\n",
215 i = i,
216 line_number = line_number,
217 nom_err = e,
218 line = line,
219 caret = '^',
220 column = column_number,
221 ),
222 }
223 }
224 .unwrap();
226 }
227
228 result
229}
230
231#[test]
232fn convert_error_panic() {
233 use nom::character::complete::char;
234 use nom::IResult;
235
236 let input = "";
237
238 let _result: IResult<_, _, VerboseError<&str>> = char('x')(input);
239}
240
241#[test]
242fn issue_1027_convert_error_panic_nonempty() {
243 use nom::character::complete::char;
244 use nom::sequence::pair;
245 use nom::Err;
246 use nom::IResult;
247 use nom::Parser;
248
249 let input = "a";
250
251 let result: IResult<_, _, VerboseError<&str>> = pair(char('a'), char('b')).parse(input);
252 let err = match result.unwrap_err() {
253 Err::Error(e) => e,
254 _ => unreachable!(),
255 };
256
257 let msg = convert_error(input, err);
258 assert_eq!(
259 msg,
260 "0: at line 1:\na\n ^\nexpected \'b\', got end of input\n\n"
261 );
262}