csv/
error.rs

1use std::{error::Error as StdError, fmt, io, result};
2
3use crate::{
4    byte_record::{ByteRecord, Position},
5    deserializer::DeserializeError,
6};
7
8/// A type alias for `Result<T, csv::Error>`.
9pub 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)]
20pub struct Error(Box<ErrorKind>);
21
22impl Error {
23    /// A crate private constructor for `Error`.
24    pub(crate) fn new(kind: ErrorKind) -> Error {
25        Error(Box::new(kind))
26    }
27
28    /// Return the specific type of this error.
29    pub fn kind(&self) -> &ErrorKind {
30        &self.0
31    }
32
33    /// Unwrap this error into its underlying type.
34    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`.
42    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`.
53    pub fn position(&self) -> Option<&Position> {
54        self.0.position()
55    }
56}
57
58/// The specific type of an error.
59#[derive(Debug)]
60pub 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
109impl 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`.
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
124impl From<io::Error> for Error {
125    fn from(err: io::Error) -> Error {
126        Error::new(ErrorKind::Io(err))
127    }
128}
129
130impl From<Error> for io::Error {
131    fn from(err: Error) -> io::Error {
132        io::Error::new(io::ErrorKind::Other, err)
133    }
134}
135
136impl StdError for Error {}
137
138impl fmt::Display for Error {
139    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)]
210pub struct FromUtf8Error {
211    record: ByteRecord,
212    err: Utf8Error,
213}
214
215impl FromUtf8Error {
216    /// Create a new FromUtf8Error.
217    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.
222    pub fn into_byte_record(self) -> ByteRecord {
223        self.record
224    }
225
226    /// Access the underlying UTF-8 validation error.
227    pub fn utf8_error(&self) -> &Utf8Error {
228        &self.err
229    }
230}
231
232impl fmt::Display for FromUtf8Error {
233    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
234        self.err.fmt(f)
235    }
236}
237
238impl StdError for FromUtf8Error {
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)]
252pub 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.
260pub fn new_utf8_error(field: usize, valid_up_to: usize) -> Utf8Error {
261    Utf8Error { field, valid_up_to }
262}
263
264impl Utf8Error {
265    /// The field index of a byte record in which UTF-8 validation failed.
266    pub fn field(&self) -> usize {
267        self.field
268    }
269    /// The index into the given field up to which valid UTF-8 was verified.
270    pub fn valid_up_to(&self) -> usize {
271        self.valid_up_to
272    }
273}
274
275impl StdError for Utf8Error {}
276
277impl fmt::Display for Utf8Error {
278    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.
294pub struct IntoInnerError<W> {
295    wtr: W,
296    err: io::Error,
297}
298
299impl<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.)
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.
311    pub fn error(&self) -> &io::Error {
312        &self.err
313    }
314
315    /// Consumes the [`IntoInnerError`] and returns the error which caused the
316    /// call to [`Writer::into_inner`](crate::Writer::into_inner) to fail.
317    ///
318    /// Unlike [`IntoInnerError::error`], this can be used to obtain ownership
319    /// of the underlying error.
320    pub fn into_error(self) -> io::Error {
321        self.err
322    }
323
324    /// Returns the underlying writer which generated the error.
325    ///
326    /// The returned value can be used for error recovery, such as
327    /// re-inspecting the buffer.
328    pub fn into_inner(self) -> W {
329        self.wtr
330    }
331}
332
333impl<W: std::any::Any> StdError for IntoInnerError<W> {}
334
335impl<W> fmt::Display for IntoInnerError<W> {
336    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
337        self.err.fmt(f)
338    }
339}
340
341impl<W> fmt::Debug for IntoInnerError<W> {
342    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
343        self.err.fmt(f)
344    }
345}