1use bstr::{BStr, ByteSlice};
6use joinery::prelude::*;
7use nom::{
8 branch::alt,
9 bytes::streaming::tag,
10 character::streaming::char,
11 combinator::{opt, recognize},
12 error::{ContextError, ErrorKind, FromExternalError, ParseError},
13 multi::many1,
14 sequence::{delimited, pair},
15};
16
17use std::fmt::Display;
18
19use crate::from_sql::FromSql;
20
21#[derive(Debug, Clone, Eq, PartialEq)]
23pub enum ParseTypeContext<'a> {
24 Single {
25 input: &'a BStr,
26 label: &'static str,
27 },
28 Alternatives {
29 input: &'a BStr,
30 labels: Vec<&'static str>,
31 },
32}
33
34impl<'a> ParseTypeContext<'a> {
35 fn push(&mut self, other: Self) {
36 match self {
37 ParseTypeContext::Single { label: label1, .. } => match other {
38 ParseTypeContext::Single {
39 input,
40 label: label2,
41 } => {
42 *self = ParseTypeContext::Alternatives {
43 input,
44 labels: vec![label1, label2],
45 }
46 }
47 ParseTypeContext::Alternatives {
48 input,
49 labels: mut labels2,
50 } => {
51 labels2.insert(0, label1);
52 *self = ParseTypeContext::Alternatives {
53 input,
54 labels: labels2,
55 }
56 }
57 },
58 ParseTypeContext::Alternatives {
59 labels: labels1, ..
60 } => match other {
61 ParseTypeContext::Single { label: label2, .. } => {
62 labels1.push(label2);
63 }
64 ParseTypeContext::Alternatives {
65 labels: labels2, ..
66 } => {
67 labels1.extend(labels2);
68 }
69 },
70 }
71 }
72}
73
74#[derive(Debug, Clone, Eq, PartialEq)]
79pub enum Error<'a> {
80 ErrorKind { input: &'a BStr, kind: ErrorKind },
81 ErrorWithContexts(Vec<ParseTypeContext<'a>>),
82}
83
84impl<'a> ParseError<&'a [u8]> for Error<'a> {
85 fn from_error_kind(input: &'a [u8], kind: ErrorKind) -> Self {
86 Self::ErrorKind {
87 input: input.into(),
88 kind,
89 }
90 }
91
92 fn append(input: &'a [u8], kind: ErrorKind, other: Self) -> Self {
94 match other {
95 Self::ErrorKind { .. } => Self::from_error_kind(input, kind),
96 e @ Self::ErrorWithContexts(_) => e,
97 }
98 }
99
100 fn from_char(input: &'a [u8], _: char) -> Self {
101 Self::from_error_kind(input, ErrorKind::Char)
102 }
103
104 fn or(self, other: Self) -> Self {
105 match self {
106 Error::ErrorKind { .. } => match other {
107 Error::ErrorKind { input, kind } => Self::from_error_kind(input, kind),
108 e @ Error::ErrorWithContexts(_) => e,
109 },
110 Error::ErrorWithContexts(mut contexts) => match other {
111 Error::ErrorKind { .. } => Error::ErrorWithContexts(contexts),
112 Error::ErrorWithContexts(mut other_contexts) => {
113 if let (Some(mut old_context), Some(new_context)) =
114 (contexts.pop(), other_contexts.pop())
115 {
116 old_context.push(new_context);
117 other_contexts.push(old_context);
118 };
119 Error::ErrorWithContexts(other_contexts)
120 }
121 },
122 }
123 }
124}
125
126impl<'a> ContextError<&'a [u8]> for Error<'a> {
127 fn add_context(input: &'a [u8], label: &'static str, other: Self) -> Self {
128 let context = ParseTypeContext::Single {
129 input: input.into(),
130 label,
131 };
132 match other {
133 Self::ErrorKind { .. } => Self::ErrorWithContexts(vec![context]),
134 Self::ErrorWithContexts(mut contexts) => {
135 contexts.push(context);
136 Self::ErrorWithContexts(contexts)
137 }
138 }
139 }
140}
141
142impl<'a, I: Into<&'a [u8]>, E> FromExternalError<I, E> for Error<'a> {
143 fn from_external_error(input: I, kind: ErrorKind, _e: E) -> Self {
144 Self::from_error_kind(input.into(), kind)
145 }
146}
147
148const INPUT_GRAPHEMES_TO_SHOW: usize = 100;
149
150impl<'a> Display for Error<'a> {
151 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152 fn show_input(input: &BStr) -> &BStr {
153 if input.is_empty() {
154 return input;
155 }
156 if input[0] == b'(' {
158 if let Ok((_, row)) = recognize(delimited(
159 char('('),
160 many1(pair(
161 alt((
162 tag("NULL"),
163 recognize(f64::from_sql),
164 recognize(i64::from_sql),
165 recognize(<Vec<u8>>::from_sql),
166 )),
167 opt(char(',')),
168 )),
169 char(')'),
170 ))(input)
171 {
172 return row.into();
173 }
174 }
175 if let Ok((_, result)) = alt((
177 tag("NULL"),
178 recognize(f64::from_sql),
179 recognize(i64::from_sql),
180 recognize(<Vec<u8>>::from_sql),
181 ))(input)
182 {
183 result.into()
184 } else {
186 let (_, end, _) = input
187 .grapheme_indices()
188 .take(INPUT_GRAPHEMES_TO_SHOW)
189 .last()
190 .expect("we have checked that input is not empty");
191 &input[..end]
192 }
193 }
194
195 match self {
196 Error::ErrorKind { input, kind } => write!(
197 f,
198 "error in {} combinator at\n\t{}",
199 kind.description(),
200 show_input(input),
201 ),
202 Error::ErrorWithContexts(contexts) => {
203 match contexts.as_slice() {
204 [] => {
205 write!(f, "unknown error")?;
206 }
207 [first, rest @ ..] => {
208 let mut last_input = match first {
209 ParseTypeContext::Single { input, label } => {
210 write!(f, "expected {} at\n\t{}\n", label, show_input(input),)?;
211 input
212 }
213 ParseTypeContext::Alternatives { input, labels } => {
214 write!(
215 f,
216 "expected {} at \n\t{}\n",
217 labels.iter().join_with(" or "),
218 show_input(input),
219 )?;
220 input
221 }
222 };
223 for context in rest {
224 let labels_joined;
225 let (displayed_label, input): (&dyn Display, _) = match context {
226 ParseTypeContext::Single { input, label } => {
227 let displayed_input = if last_input == input {
228 None
229 } else {
230 Some(input)
231 };
232 last_input = input;
233 (label, displayed_input)
234 }
235 ParseTypeContext::Alternatives { input, labels } => {
236 let displayed_input = if last_input == input {
237 None
238 } else {
239 Some(input)
240 };
241 labels_joined = labels.iter().join_with(" or ");
242 last_input = input;
243 (&labels_joined, displayed_input)
244 }
245 };
246 write!(f, "while parsing {}", displayed_label,)?;
247 if let Some(input) = input {
248 write!(f, " at\n\t{}", show_input(input),)?;
249 }
250 writeln!(f)?;
251 }
252 }
253 }
254 Ok(())
255 }
256 }
257 }
258}