use std::error::Error as StdError;
use std::fmt;
use std::io;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ErrorKind {
Io,
Corruption,
UnsupportedVersion,
UnsupportedFeature,
UnsupportedFrame,
ResourceLimit,
IncompleteTail,
InvalidArgument,
SchemaMismatch,
WriterLocked,
Poisoned,
FileReplaced,
FileTruncated,
Internal,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum ErrorContext {
File,
Prologue,
Frame {
sequence: u64,
},
Prefix,
Header,
Payload,
Trailer,
}
pub struct Error {
kind: ErrorKind,
message: String,
offset: Option<u64>,
context: Vec<ErrorContext>,
source: Option<Box<dyn StdError + Send + Sync + 'static>>,
}
impl Error {
pub fn kind(&self) -> ErrorKind {
self.kind
}
pub fn message(&self) -> &str {
&self.message
}
pub fn offset(&self) -> Option<u64> {
self.offset
}
pub fn context(&self) -> &[ErrorContext] {
&self.context
}
pub(crate) fn corruption(message: impl Into<String>, offset: Option<u64>) -> Self {
Self::new(ErrorKind::Corruption, message, offset, None)
}
pub(crate) fn unsupported_version(message: impl Into<String>, offset: Option<u64>) -> Self {
Self::new(ErrorKind::UnsupportedVersion, message, offset, None)
}
pub(crate) fn unsupported_feature(message: impl Into<String>, offset: Option<u64>) -> Self {
Self::new(ErrorKind::UnsupportedFeature, message, offset, None)
}
pub(crate) fn unsupported_frame(message: impl Into<String>, offset: Option<u64>) -> Self {
Self::new(ErrorKind::UnsupportedFrame, message, offset, None)
}
pub(crate) fn resource_limit(message: impl Into<String>, offset: Option<u64>) -> Self {
Self::new(ErrorKind::ResourceLimit, message, offset, None)
}
pub(crate) fn incomplete_tail(message: impl Into<String>, offset: Option<u64>) -> Self {
Self::new(ErrorKind::IncompleteTail, message, offset, None)
}
pub(crate) fn invalid_argument(message: impl Into<String>) -> Self {
Self::new(ErrorKind::InvalidArgument, message, None, None)
}
pub(crate) fn schema_mismatch(message: impl Into<String>) -> Self {
Self::new(ErrorKind::SchemaMismatch, message, None, None)
}
pub(crate) fn writer_locked(message: impl Into<String>) -> Self {
Self::new(ErrorKind::WriterLocked, message, None, None)
}
pub(crate) fn poisoned(message: impl Into<String>) -> Self {
Self::new(ErrorKind::Poisoned, message, None, None)
}
pub(crate) fn file_replaced(message: impl Into<String>) -> Self {
Self::new(ErrorKind::FileReplaced, message, None, None)
}
pub(crate) fn file_truncated(message: impl Into<String>, offset: Option<u64>) -> Self {
Self::new(ErrorKind::FileTruncated, message, offset, None)
}
pub(crate) fn internal(message: impl Into<String>) -> Self {
Self::new(ErrorKind::Internal, message, None, None)
}
pub(crate) fn io(error: io::Error, offset: Option<u64>) -> Self {
Self::new(
ErrorKind::Io,
error.to_string(),
offset,
Some(Box::new(error)),
)
}
pub(crate) fn with_context(mut self, context: ErrorContext) -> Self {
self.context.push(context);
self
}
pub(crate) fn with_message_prefix(mut self, prefix: impl AsRef<str>) -> Self {
self.message = format!("{}: {}", prefix.as_ref(), self.message);
self
}
fn new(
kind: ErrorKind,
message: impl Into<String>,
offset: Option<u64>,
source: Option<Box<dyn StdError + Send + Sync + 'static>>,
) -> Self {
Self {
kind,
message: message.into(),
offset,
context: Vec::new(),
source,
}
}
}
impl fmt::Debug for Error {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("Error")
.field("kind", &self.kind)
.field("message", &self.message)
.field("offset", &self.offset)
.field("context", &self.context)
.field(
"source",
&self.source.as_ref().map(|source| source.to_string()),
)
.finish()
}
}
impl fmt::Display for Error {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{}: {}", self.kind, self.message)?;
if let Some(offset) = self.offset {
write!(formatter, " at file offset 0x{offset:x}")?;
}
if !self.context.is_empty() {
write!(formatter, " (context: ")?;
for (index, context) in self.context.iter().enumerate() {
if index != 0 {
write!(formatter, ", ")?;
}
write!(formatter, "{context:?}")?;
}
write!(formatter, ")")?;
}
Ok(())
}
}
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
self.source
.as_ref()
.map(|source| &**source as &(dyn StdError + 'static))
}
}
impl fmt::Display for ErrorKind {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let label = match self {
Self::Io => "I/O error",
Self::Corruption => "corruption",
Self::UnsupportedVersion => "unsupported format version",
Self::UnsupportedFeature => "unsupported feature",
Self::UnsupportedFrame => "unsupported frame",
Self::ResourceLimit => "resource limit exceeded",
Self::IncompleteTail => "incomplete tail",
Self::InvalidArgument => "invalid argument",
Self::SchemaMismatch => "schema mismatch",
Self::WriterLocked => "writer locked",
Self::Poisoned => "poisoned writer",
Self::FileReplaced => "file replaced",
Self::FileTruncated => "file truncated",
Self::Internal => "internal writer error",
};
formatter.write_str(label)
}
}