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}