• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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