use std::error::Error as StdError; use std::fmt; use std::io; use std::result; use crate::byte_record::{ByteRecord, Position}; use crate::deserializer::DeserializeError; /// A type alias for `Result`. pub type Result = result::Result; /// An error that can occur when processing CSV data. /// /// This error can happen when writing or reading CSV data. /// /// There are some important scenarios where an error is impossible to occur. /// For example, if a CSV reader is used on an in-memory buffer with the /// `flexible` option enabled and one is reading records as raw byte strings, /// then no error can occur. #[derive(Debug)] pub struct Error(Box); impl Error { /// A crate private constructor for `Error`. pub(crate) fn new(kind: ErrorKind) -> Error { Error(Box::new(kind)) } /// Return the specific type of this error. pub fn kind(&self) -> &ErrorKind { &self.0 } /// Unwrap this error into its underlying type. pub fn into_kind(self) -> ErrorKind { *self.0 } /// Returns true if this is an I/O error. /// /// If this is true, the underlying `ErrorKind` is guaranteed to be /// `ErrorKind::Io`. pub fn is_io_error(&self) -> bool { match *self.0 { ErrorKind::Io(_) => true, _ => false, } } /// Return the position for this error, if one exists. /// /// This is a convenience function that permits callers to easily access /// the position on an error without doing case analysis on `ErrorKind`. pub fn position(&self) -> Option<&Position> { self.0.position() } } /// The specific type of an error. #[derive(Debug)] pub enum ErrorKind { /// An I/O error that occurred while reading CSV data. Io(io::Error), /// A UTF-8 decoding error that occured while reading CSV data into Rust /// `String`s. Utf8 { /// The position of the record in which this error occurred, if /// available. pos: Option, /// The corresponding UTF-8 error. err: Utf8Error, }, /// This error occurs when two records with an unequal number of fields /// are found. This error only occurs when the `flexible` option in a /// CSV reader/writer is disabled. UnequalLengths { /// The position of the first record with an unequal number of fields /// to the previous record, if available. pos: Option, /// The expected number of fields in a record. This is the number of /// fields in the record read prior to the record indicated by /// `pos`. expected_len: u64, /// The number of fields in the bad record. len: u64, }, /// This error occurs when either the `byte_headers` or `headers` methods /// are called on a CSV reader that was asked to `seek` before it parsed /// the first record. Seek, /// An error of this kind occurs only when using the Serde serializer. Serialize(String), /// An error of this kind occurs only when performing automatic /// deserialization with serde. Deserialize { /// The position of this error, if available. pos: Option, /// The deserialization error. err: DeserializeError, }, /// Hints that destructuring should not be exhaustive. /// /// This enum may grow additional variants, so this makes sure clients /// don't count on exhaustive matching. (Otherwise, adding a new variant /// could break existing code.) #[doc(hidden)] __Nonexhaustive, } impl ErrorKind { /// Return the position for this error, if one exists. /// /// This is a convenience function that permits callers to easily access /// the position on an error without doing case analysis on `ErrorKind`. pub fn position(&self) -> Option<&Position> { match *self { ErrorKind::Utf8 { ref pos, .. } => pos.as_ref(), ErrorKind::UnequalLengths { ref pos, .. } => pos.as_ref(), ErrorKind::Deserialize { ref pos, .. } => pos.as_ref(), _ => None, } } } impl From for Error { fn from(err: io::Error) -> Error { Error::new(ErrorKind::Io(err)) } } impl From for io::Error { fn from(err: Error) -> io::Error { io::Error::new(io::ErrorKind::Other, err) } } impl StdError for Error { fn source(&self) -> Option<&(dyn StdError + 'static)> { match *self.0 { ErrorKind::Io(ref err) => Some(err), ErrorKind::Utf8 { ref err, .. } => Some(err), ErrorKind::UnequalLengths { .. } => None, ErrorKind::Seek => None, ErrorKind::Serialize(_) => None, ErrorKind::Deserialize { ref err, .. } => Some(err), _ => unreachable!(), } } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self.0 { ErrorKind::Io(ref err) => err.fmt(f), ErrorKind::Utf8 { pos: None, ref err } => { write!(f, "CSV parse error: field {}: {}", err.field(), err) } ErrorKind::Utf8 { pos: Some(ref pos), ref err } => write!( f, "CSV parse error: record {} \ (line {}, field: {}, byte: {}): {}", pos.record(), pos.line(), err.field(), pos.byte(), err ), ErrorKind::UnequalLengths { pos: None, expected_len, len } => { write!( f, "CSV error: \ found record with {} fields, but the previous record \ has {} fields", len, expected_len ) } ErrorKind::UnequalLengths { pos: Some(ref pos), expected_len, len, } => write!( f, "CSV error: record {} (line: {}, byte: {}): \ found record with {} fields, but the previous record \ has {} fields", pos.record(), pos.line(), pos.byte(), len, expected_len ), ErrorKind::Seek => write!( f, "CSV error: cannot access headers of CSV data \ when the parser was seeked before the first record \ could be read" ), ErrorKind::Serialize(ref err) => { write!(f, "CSV write error: {}", err) } ErrorKind::Deserialize { pos: None, ref err } => { write!(f, "CSV deserialize error: {}", err) } ErrorKind::Deserialize { pos: Some(ref pos), ref err } => write!( f, "CSV deserialize error: record {} \ (line: {}, byte: {}): {}", pos.record(), pos.line(), pos.byte(), err ), _ => unreachable!(), } } } /// A UTF-8 validation error during record conversion. /// /// This occurs when attempting to convert a `ByteRecord` into a /// `StringRecord`. #[derive(Clone, Debug, Eq, PartialEq)] pub struct FromUtf8Error { record: ByteRecord, err: Utf8Error, } impl FromUtf8Error { /// Create a new FromUtf8Error. pub(crate) fn new(rec: ByteRecord, err: Utf8Error) -> FromUtf8Error { FromUtf8Error { record: rec, err: err } } /// Access the underlying `ByteRecord` that failed UTF-8 validation. pub fn into_byte_record(self) -> ByteRecord { self.record } /// Access the underlying UTF-8 validation error. pub fn utf8_error(&self) -> &Utf8Error { &self.err } } impl fmt::Display for FromUtf8Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.err.fmt(f) } } impl StdError for FromUtf8Error { fn source(&self) -> Option<&(dyn StdError + 'static)> { Some(&self.err) } } /// A UTF-8 validation error. /// /// This occurs when attempting to convert a `ByteRecord` into a /// `StringRecord`. /// /// The error includes the index of the field that failed validation, and the /// last byte at which valid UTF-8 was verified. #[derive(Clone, Debug, Eq, PartialEq)] pub struct Utf8Error { /// The field index of a byte record in which UTF-8 validation failed. field: usize, /// The index into the given field up to which valid UTF-8 was verified. valid_up_to: usize, } /// Create a new UTF-8 error. pub fn new_utf8_error(field: usize, valid_up_to: usize) -> Utf8Error { Utf8Error { field: field, valid_up_to: valid_up_to } } impl Utf8Error { /// The field index of a byte record in which UTF-8 validation failed. pub fn field(&self) -> usize { self.field } /// The index into the given field up to which valid UTF-8 was verified. pub fn valid_up_to(&self) -> usize { self.valid_up_to } } impl StdError for Utf8Error {} impl fmt::Display for Utf8Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "invalid utf-8: invalid UTF-8 in field {} near byte index {}", self.field, self.valid_up_to ) } } /// `IntoInnerError` occurs when consuming a `Writer` fails. /// /// Consuming the `Writer` causes a flush to happen. If the flush fails, then /// this error is returned, which contains both the original `Writer` and /// the error that occurred. /// /// The type parameter `W` is the unconsumed writer. pub struct IntoInnerError { wtr: W, err: io::Error, } impl IntoInnerError { /// Creates a new `IntoInnerError`. /// /// (This is a visibility hack. It's public in this module, but not in the /// crate.) pub(crate) fn new(wtr: W, err: io::Error) -> IntoInnerError { IntoInnerError { wtr: wtr, err: err } } /// Returns the error which caused the call to `into_inner` to fail. /// /// This error was returned when attempting to flush the internal buffer. pub fn error(&self) -> &io::Error { &self.err } /// Returns the underlying writer which generated the error. /// /// The returned value can be used for error recovery, such as /// re-inspecting the buffer. pub fn into_inner(self) -> W { self.wtr } } impl StdError for IntoInnerError { fn source(&self) -> Option<&(dyn StdError + 'static)> { self.err.source() } } impl fmt::Display for IntoInnerError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.err.fmt(f) } } impl fmt::Debug for IntoInnerError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.err.fmt(f) } }