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