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}