1 use std::error::Error as StdError;
2 use std::fmt;
3 use std::io;
4 use std::result;
5
6 use crate::byte_record::{ByteRecord, Position};
7 use crate::deserializer::DeserializeError;
8
9 /// A type alias for `Result<T, csv::Error>`.
10 pub type Result<T> = result::Result<T, Error>;
11
12 /// An error that can occur when processing CSV data.
13 ///
14 /// This error can happen when writing or reading CSV data.
15 ///
16 /// There are some important scenarios where an error is impossible to occur.
17 /// For example, if a CSV reader is used on an in-memory buffer with the
18 /// `flexible` option enabled and one is reading records as raw byte strings,
19 /// then no error can occur.
20 #[derive(Debug)]
21 pub struct Error(Box<ErrorKind>);
22
23 impl Error {
24 /// A crate private constructor for `Error`.
new(kind: ErrorKind) -> Error25 pub(crate) fn new(kind: ErrorKind) -> Error {
26 Error(Box::new(kind))
27 }
28
29 /// Return the specific type of this error.
kind(&self) -> &ErrorKind30 pub fn kind(&self) -> &ErrorKind {
31 &self.0
32 }
33
34 /// Unwrap this error into its underlying type.
into_kind(self) -> ErrorKind35 pub fn into_kind(self) -> ErrorKind {
36 *self.0
37 }
38
39 /// Returns true if this is an I/O error.
40 ///
41 /// If this is true, the underlying `ErrorKind` is guaranteed to be
42 /// `ErrorKind::Io`.
is_io_error(&self) -> bool43 pub fn is_io_error(&self) -> bool {
44 match *self.0 {
45 ErrorKind::Io(_) => true,
46 _ => false,
47 }
48 }
49
50 /// Return the position for this error, if one exists.
51 ///
52 /// This is a convenience function that permits callers to easily access
53 /// the position on an error without doing case analysis on `ErrorKind`.
position(&self) -> Option<&Position>54 pub fn position(&self) -> Option<&Position> {
55 self.0.position()
56 }
57 }
58
59 /// The specific type of an error.
60 #[derive(Debug)]
61 pub enum ErrorKind {
62 /// An I/O error that occurred while reading CSV data.
63 Io(io::Error),
64 /// A UTF-8 decoding error that occured while reading CSV data into Rust
65 /// `String`s.
66 Utf8 {
67 /// The position of the record in which this error occurred, if
68 /// available.
69 pos: Option<Position>,
70 /// The corresponding UTF-8 error.
71 err: Utf8Error,
72 },
73 /// This error occurs when two records with an unequal number of fields
74 /// are found. This error only occurs when the `flexible` option in a
75 /// CSV reader/writer is disabled.
76 UnequalLengths {
77 /// The position of the first record with an unequal number of fields
78 /// to the previous record, if available.
79 pos: Option<Position>,
80 /// The expected number of fields in a record. This is the number of
81 /// fields in the record read prior to the record indicated by
82 /// `pos`.
83 expected_len: u64,
84 /// The number of fields in the bad record.
85 len: u64,
86 },
87 /// This error occurs when either the `byte_headers` or `headers` methods
88 /// are called on a CSV reader that was asked to `seek` before it parsed
89 /// the first record.
90 Seek,
91 /// An error of this kind occurs only when using the Serde serializer.
92 Serialize(String),
93 /// An error of this kind occurs only when performing automatic
94 /// deserialization with serde.
95 Deserialize {
96 /// The position of this error, if available.
97 pos: Option<Position>,
98 /// The deserialization error.
99 err: DeserializeError,
100 },
101 /// Hints that destructuring should not be exhaustive.
102 ///
103 /// This enum may grow additional variants, so this makes sure clients
104 /// don't count on exhaustive matching. (Otherwise, adding a new variant
105 /// could break existing code.)
106 #[doc(hidden)]
107 __Nonexhaustive,
108 }
109
110 impl ErrorKind {
111 /// Return the position for this error, if one exists.
112 ///
113 /// This is a convenience function that permits callers to easily access
114 /// the position on an error without doing case analysis on `ErrorKind`.
position(&self) -> Option<&Position>115 pub fn position(&self) -> Option<&Position> {
116 match *self {
117 ErrorKind::Utf8 { ref pos, .. } => pos.as_ref(),
118 ErrorKind::UnequalLengths { ref pos, .. } => pos.as_ref(),
119 ErrorKind::Deserialize { ref pos, .. } => pos.as_ref(),
120 _ => None,
121 }
122 }
123 }
124
125 impl From<io::Error> for Error {
from(err: io::Error) -> Error126 fn from(err: io::Error) -> Error {
127 Error::new(ErrorKind::Io(err))
128 }
129 }
130
131 impl From<Error> for io::Error {
from(err: Error) -> io::Error132 fn from(err: Error) -> io::Error {
133 io::Error::new(io::ErrorKind::Other, err)
134 }
135 }
136
137 impl StdError for Error {
source(&self) -> Option<&(dyn StdError + 'static)>138 fn source(&self) -> Option<&(dyn StdError + 'static)> {
139 match *self.0 {
140 ErrorKind::Io(ref err) => Some(err),
141 ErrorKind::Utf8 { ref err, .. } => Some(err),
142 ErrorKind::UnequalLengths { .. } => None,
143 ErrorKind::Seek => None,
144 ErrorKind::Serialize(_) => None,
145 ErrorKind::Deserialize { ref err, .. } => Some(err),
146 _ => unreachable!(),
147 }
148 }
149 }
150
151 impl fmt::Display for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result152 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
153 match *self.0 {
154 ErrorKind::Io(ref err) => err.fmt(f),
155 ErrorKind::Utf8 { pos: None, ref err } => {
156 write!(f, "CSV parse error: field {}: {}", err.field(), err)
157 }
158 ErrorKind::Utf8 { pos: Some(ref pos), ref err } => write!(
159 f,
160 "CSV parse error: record {} \
161 (line {}, field: {}, byte: {}): {}",
162 pos.record(),
163 pos.line(),
164 err.field(),
165 pos.byte(),
166 err
167 ),
168 ErrorKind::UnequalLengths { pos: None, expected_len, len } => {
169 write!(
170 f,
171 "CSV error: \
172 found record with {} fields, but the previous record \
173 has {} fields",
174 len, expected_len
175 )
176 }
177 ErrorKind::UnequalLengths {
178 pos: Some(ref pos),
179 expected_len,
180 len,
181 } => write!(
182 f,
183 "CSV error: record {} (line: {}, byte: {}): \
184 found record with {} fields, but the previous record \
185 has {} fields",
186 pos.record(),
187 pos.line(),
188 pos.byte(),
189 len,
190 expected_len
191 ),
192 ErrorKind::Seek => write!(
193 f,
194 "CSV error: cannot access headers of CSV data \
195 when the parser was seeked before the first record \
196 could be read"
197 ),
198 ErrorKind::Serialize(ref err) => {
199 write!(f, "CSV write error: {}", err)
200 }
201 ErrorKind::Deserialize { pos: None, ref err } => {
202 write!(f, "CSV deserialize error: {}", err)
203 }
204 ErrorKind::Deserialize { pos: Some(ref pos), ref err } => write!(
205 f,
206 "CSV deserialize error: record {} \
207 (line: {}, byte: {}): {}",
208 pos.record(),
209 pos.line(),
210 pos.byte(),
211 err
212 ),
213 _ => unreachable!(),
214 }
215 }
216 }
217
218 /// A UTF-8 validation error during record conversion.
219 ///
220 /// This occurs when attempting to convert a `ByteRecord` into a
221 /// `StringRecord`.
222 #[derive(Clone, Debug, Eq, PartialEq)]
223 pub struct FromUtf8Error {
224 record: ByteRecord,
225 err: Utf8Error,
226 }
227
228 impl FromUtf8Error {
229 /// Create a new FromUtf8Error.
new(rec: ByteRecord, err: Utf8Error) -> FromUtf8Error230 pub(crate) fn new(rec: ByteRecord, err: Utf8Error) -> FromUtf8Error {
231 FromUtf8Error { record: rec, err: err }
232 }
233
234 /// Access the underlying `ByteRecord` that failed UTF-8 validation.
into_byte_record(self) -> ByteRecord235 pub fn into_byte_record(self) -> ByteRecord {
236 self.record
237 }
238
239 /// Access the underlying UTF-8 validation error.
utf8_error(&self) -> &Utf8Error240 pub fn utf8_error(&self) -> &Utf8Error {
241 &self.err
242 }
243 }
244
245 impl fmt::Display for FromUtf8Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result246 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
247 self.err.fmt(f)
248 }
249 }
250
251 impl StdError for FromUtf8Error {
source(&self) -> Option<&(dyn StdError + 'static)>252 fn source(&self) -> Option<&(dyn StdError + 'static)> {
253 Some(&self.err)
254 }
255 }
256
257 /// A UTF-8 validation error.
258 ///
259 /// This occurs when attempting to convert a `ByteRecord` into a
260 /// `StringRecord`.
261 ///
262 /// The error includes the index of the field that failed validation, and the
263 /// last byte at which valid UTF-8 was verified.
264 #[derive(Clone, Debug, Eq, PartialEq)]
265 pub struct Utf8Error {
266 /// The field index of a byte record in which UTF-8 validation failed.
267 field: usize,
268 /// The index into the given field up to which valid UTF-8 was verified.
269 valid_up_to: usize,
270 }
271
272 /// Create a new UTF-8 error.
new_utf8_error(field: usize, valid_up_to: usize) -> Utf8Error273 pub fn new_utf8_error(field: usize, valid_up_to: usize) -> Utf8Error {
274 Utf8Error { field: field, valid_up_to: valid_up_to }
275 }
276
277 impl Utf8Error {
278 /// The field index of a byte record in which UTF-8 validation failed.
field(&self) -> usize279 pub fn field(&self) -> usize {
280 self.field
281 }
282 /// The index into the given field up to which valid UTF-8 was verified.
valid_up_to(&self) -> usize283 pub fn valid_up_to(&self) -> usize {
284 self.valid_up_to
285 }
286 }
287
288 impl StdError for Utf8Error {}
289
290 impl fmt::Display for Utf8Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result291 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
292 write!(
293 f,
294 "invalid utf-8: invalid UTF-8 in field {} near byte index {}",
295 self.field, self.valid_up_to
296 )
297 }
298 }
299
300 /// `IntoInnerError` occurs when consuming a `Writer` fails.
301 ///
302 /// Consuming the `Writer` causes a flush to happen. If the flush fails, then
303 /// this error is returned, which contains both the original `Writer` and
304 /// the error that occurred.
305 ///
306 /// The type parameter `W` is the unconsumed writer.
307 pub struct IntoInnerError<W> {
308 wtr: W,
309 err: io::Error,
310 }
311
312 impl<W> IntoInnerError<W> {
313 /// Creates a new `IntoInnerError`.
314 ///
315 /// (This is a visibility hack. It's public in this module, but not in the
316 /// crate.)
new(wtr: W, err: io::Error) -> IntoInnerError<W>317 pub(crate) fn new(wtr: W, err: io::Error) -> IntoInnerError<W> {
318 IntoInnerError { wtr: wtr, err: err }
319 }
320
321 /// Returns the error which caused the call to `into_inner` to fail.
322 ///
323 /// This error was returned when attempting to flush the internal buffer.
error(&self) -> &io::Error324 pub fn error(&self) -> &io::Error {
325 &self.err
326 }
327
328 /// Returns the underlying writer which generated the error.
329 ///
330 /// The returned value can be used for error recovery, such as
331 /// re-inspecting the buffer.
into_inner(self) -> W332 pub fn into_inner(self) -> W {
333 self.wtr
334 }
335 }
336
337 impl<W: std::any::Any> StdError for IntoInnerError<W> {
source(&self) -> Option<&(dyn StdError + 'static)>338 fn source(&self) -> Option<&(dyn StdError + 'static)> {
339 self.err.source()
340 }
341 }
342
343 impl<W> fmt::Display for IntoInnerError<W> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result344 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
345 self.err.fmt(f)
346 }
347 }
348
349 impl<W> fmt::Debug for IntoInnerError<W> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result350 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
351 self.err.fmt(f)
352 }
353 }
354