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