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        matches!(*self.0, ErrorKind::Io(_))
44    }
45
46    /// Return the position for this error, if one exists.
47    ///
48    /// This is a convenience function that permits callers to easily access
49    /// the position on an error without doing case analysis on `ErrorKind`.
50    pub fn position(&self) -> Option<&Position> {
51        self.0.position()
52    }
53}
54
55/// The specific type of an error.
56#[derive(Debug)]
57#[non_exhaustive]
58pub enum ErrorKind {
59    /// An I/O error that occurred while reading CSV data.
60    Io(io::Error),
61    /// A UTF-8 decoding error that occured while reading CSV data into Rust
62    /// `String`s.
63    Utf8 {
64        /// The position of the record in which this error occurred, if
65        /// available.
66        pos: Option<Position>,
67        /// The corresponding UTF-8 error.
68        err: Utf8Error,
69    },
70    /// This error occurs when two records with an unequal number of fields
71    /// are found. This error only occurs when the `flexible` option in a
72    /// CSV reader/writer is disabled.
73    UnequalLengths {
74        /// The position of the first record with an unequal number of fields
75        /// to the previous record, if available.
76        pos: Option<Position>,
77        /// The expected number of fields in a record. This is the number of
78        /// fields in the record read prior to the record indicated by
79        /// `pos`.
80        expected_len: u64,
81        /// The number of fields in the bad record.
82        len: u64,
83    },
84    /// This error occurs when either the `byte_headers` or `headers` methods
85    /// are called on a CSV reader that was asked to `seek` before it parsed
86    /// the first record.
87    Seek,
88    /// An error of this kind occurs only when using the Serde serializer.
89    Serialize(String),
90    /// An error of this kind occurs only when performing automatic
91    /// deserialization with serde.
92    Deserialize {
93        /// The position of this error, if available.
94        pos: Option<Position>,
95        /// The deserialization error.
96        err: DeserializeError,
97    },
98}
99
100impl ErrorKind {
101    /// Return the position for this error, if one exists.
102    ///
103    /// This is a convenience function that permits callers to easily access
104    /// the position on an error without doing case analysis on `ErrorKind`.
105    pub fn position(&self) -> Option<&Position> {
106        match *self {
107            ErrorKind::Utf8 { ref pos, .. } => pos.as_ref(),
108            ErrorKind::UnequalLengths { ref pos, .. } => pos.as_ref(),
109            ErrorKind::Deserialize { ref pos, .. } => pos.as_ref(),
110            _ => None,
111        }
112    }
113}
114
115impl From<io::Error> for Error {
116    fn from(err: io::Error) -> Error {
117        Error::new(ErrorKind::Io(err))
118    }
119}
120
121impl From<Error> for io::Error {
122    fn from(err: Error) -> io::Error {
123        io::Error::new(io::ErrorKind::Other, err)
124    }
125}
126
127impl StdError for Error {}
128
129impl fmt::Display for Error {
130    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
131        match *self.0 {
132            ErrorKind::Io(ref err) => err.fmt(f),
133            ErrorKind::Utf8 { pos: None, ref err } => {
134                write!(f, "CSV parse error: field {}: {}", err.field(), err)
135            }
136            ErrorKind::Utf8 { pos: Some(ref pos), ref err } => write!(
137                f,
138                "CSV parse error: record {} \
139                 (line {}, field: {}, byte: {}): {}",
140                pos.record(),
141                pos.line(),
142                err.field(),
143                pos.byte(),
144                err
145            ),
146            ErrorKind::UnequalLengths { pos: None, expected_len, len } => {
147                write!(
148                    f,
149                    "CSV error: \
150                     found record with {} fields, but the previous record \
151                     has {} fields",
152                    len, expected_len
153                )
154            }
155            ErrorKind::UnequalLengths {
156                pos: Some(ref pos),
157                expected_len,
158                len,
159            } => write!(
160                f,
161                "CSV error: record {} (line: {}, byte: {}): \
162                 found record with {} fields, but the previous record \
163                 has {} fields",
164                pos.record(),
165                pos.line(),
166                pos.byte(),
167                len,
168                expected_len
169            ),
170            ErrorKind::Seek => write!(
171                f,
172                "CSV error: cannot access headers of CSV data \
173                 when the parser was seeked before the first record \
174                 could be read"
175            ),
176            ErrorKind::Serialize(ref err) => {
177                write!(f, "CSV write error: {}", err)
178            }
179            ErrorKind::Deserialize { pos: None, ref err } => {
180                write!(f, "CSV deserialize error: {}", err)
181            }
182            ErrorKind::Deserialize { pos: Some(ref pos), ref err } => write!(
183                f,
184                "CSV deserialize error: record {} \
185                 (line: {}, byte: {}): {}",
186                pos.record(),
187                pos.line(),
188                pos.byte(),
189                err
190            ),
191        }
192    }
193}
194
195/// A UTF-8 validation error during record conversion.
196///
197/// This occurs when attempting to convert a `ByteRecord` into a
198/// `StringRecord`.
199#[derive(Clone, Debug, Eq, PartialEq)]
200pub struct FromUtf8Error {
201    record: ByteRecord,
202    err: Utf8Error,
203}
204
205impl FromUtf8Error {
206    /// Create a new FromUtf8Error.
207    pub(crate) fn new(record: ByteRecord, err: Utf8Error) -> FromUtf8Error {
208        FromUtf8Error { record, err }
209    }
210
211    /// Access the underlying `ByteRecord` that failed UTF-8 validation.
212    pub fn into_byte_record(self) -> ByteRecord {
213        self.record
214    }
215
216    /// Access the underlying UTF-8 validation error.
217    pub fn utf8_error(&self) -> &Utf8Error {
218        &self.err
219    }
220}
221
222impl fmt::Display for FromUtf8Error {
223    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
224        self.err.fmt(f)
225    }
226}
227
228impl StdError for FromUtf8Error {
229    fn source(&self) -> Option<&(dyn StdError + 'static)> {
230        Some(&self.err)
231    }
232}
233
234/// A UTF-8 validation error.
235///
236/// This occurs when attempting to convert a `ByteRecord` into a
237/// `StringRecord`.
238///
239/// The error includes the index of the field that failed validation, and the
240/// last byte at which valid UTF-8 was verified.
241#[derive(Clone, Debug, Eq, PartialEq)]
242pub struct Utf8Error {
243    /// The field index of a byte record in which UTF-8 validation failed.
244    field: usize,
245    /// The index into the given field up to which valid UTF-8 was verified.
246    valid_up_to: usize,
247}
248
249/// Create a new UTF-8 error.
250pub fn new_utf8_error(field: usize, valid_up_to: usize) -> Utf8Error {
251    Utf8Error { field, valid_up_to }
252}
253
254impl Utf8Error {
255    /// The field index of a byte record in which UTF-8 validation failed.
256    pub fn field(&self) -> usize {
257        self.field
258    }
259    /// The index into the given field up to which valid UTF-8 was verified.
260    pub fn valid_up_to(&self) -> usize {
261        self.valid_up_to
262    }
263}
264
265impl StdError for Utf8Error {}
266
267impl fmt::Display for Utf8Error {
268    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
269        write!(
270            f,
271            "invalid utf-8: invalid UTF-8 in field {} near byte index {}",
272            self.field, self.valid_up_to
273        )
274    }
275}
276
277/// `IntoInnerError` occurs when consuming a `Writer` fails.
278///
279/// Consuming the `Writer` causes a flush to happen. If the flush fails, then
280/// this error is returned, which contains both the original `Writer` and
281/// the error that occurred.
282///
283/// The type parameter `W` is the unconsumed writer.
284pub struct IntoInnerError<W> {
285    wtr: W,
286    err: io::Error,
287}
288
289impl<W> IntoInnerError<W> {
290    /// Creates a new `IntoInnerError`.
291    ///
292    /// (This is a visibility hack. It's public in this module, but not in the
293    /// crate.)
294    pub(crate) fn new(wtr: W, err: io::Error) -> IntoInnerError<W> {
295        IntoInnerError { wtr, err }
296    }
297
298    /// Returns the error which caused the call to `into_inner` to fail.
299    ///
300    /// This error was returned when attempting to flush the internal buffer.
301    pub fn error(&self) -> &io::Error {
302        &self.err
303    }
304
305    /// Consumes the [`IntoInnerError`] and returns the error which caused the
306    /// call to [`Writer::into_inner`](crate::Writer::into_inner) to fail.
307    ///
308    /// Unlike [`IntoInnerError::error`], this can be used to obtain ownership
309    /// of the underlying error.
310    pub fn into_error(self) -> io::Error {
311        self.err
312    }
313
314    /// Returns the underlying writer which generated the error.
315    ///
316    /// The returned value can be used for error recovery, such as
317    /// re-inspecting the buffer.
318    pub fn into_inner(self) -> W {
319        self.wtr
320    }
321}
322
323impl<W: std::any::Any> StdError for IntoInnerError<W> {}
324
325impl<W> fmt::Display for IntoInnerError<W> {
326    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
327        self.err.fmt(f)
328    }
329}
330
331impl<W> fmt::Debug for IntoInnerError<W> {
332    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
333        self.err.fmt(f)
334    }
335}