preflate_rs/
preflate_error.rsuse std::fmt::Display;
use std::io::ErrorKind;
use std::num::TryFromIntError;
#[derive(Debug, Clone, Copy, PartialEq)]
#[non_exhaustive]
pub enum ExitCode {
ReadDeflate = 1,
InvalidPredictionData = 2,
AnalyzeFailed = 3,
RecompressFailed = 4,
RoundtripMismatch = 5,
ReadBlock = 6,
PredictBlock = 7,
PredictTree = 8,
RecreateBlock = 9,
RecreateTree = 10,
EncodeBlock = 11,
InvalidCompressedWrapper = 12,
ZstdError = 14,
InvalidParameterHeader = 15,
ShortRead = 16,
OsError = 17,
GeneralFailure = 18,
InvalidIDat = 19,
MatchNotFound = 20,
InvalidDeflate = 21,
NoCompressionCandidates = 22,
InvalidParameter = 23,
AssertionFailure = 24,
}
impl Display for ExitCode {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
impl ExitCode {
pub fn as_integer_error_code(self) -> i32 {
self as i32
}
}
#[derive(Debug, Clone)]
struct PreflateErrorInternal {
exit_code: ExitCode,
message: String,
}
#[derive(Debug, Clone)]
pub struct PreflateError {
i: Box<PreflateErrorInternal>,
}
pub type Result<T> = std::result::Result<T, PreflateError>;
impl Display for PreflateError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{0}: {1}", self.i.exit_code, self.i.message)
}
}
impl PreflateError {
pub fn new(exit_code: ExitCode, message: &str) -> PreflateError {
PreflateError {
i: Box::new(PreflateErrorInternal {
exit_code,
message: message.to_owned(),
}),
}
}
pub fn exit_code(&self) -> ExitCode {
self.i.exit_code
}
pub fn message(&self) -> &str {
&self.i.message
}
#[cold]
#[inline(never)]
#[track_caller]
pub fn add_context(&mut self) {
self.i
.message
.push_str(&format!("\n at {}", std::panic::Location::caller()));
}
}
#[cold]
#[track_caller]
pub fn err_exit_code<T>(error_code: ExitCode, message: &str) -> Result<T> {
let mut e = PreflateError::new(error_code, message);
e.add_context();
return Err(e);
}
pub trait AddContext<T> {
#[track_caller]
fn context(self) -> Result<T>;
fn with_context<FN: Fn() -> String>(self, f: FN) -> Result<T>;
}
impl<T, E: Into<PreflateError>> AddContext<T> for core::result::Result<T, E> {
#[track_caller]
fn context(self) -> Result<T> {
match self {
Ok(x) => Ok(x),
Err(e) => {
let mut e = e.into();
e.add_context();
Err(e)
}
}
}
#[track_caller]
fn with_context<FN: Fn() -> String>(self, f: FN) -> Result<T> {
match self {
Ok(x) => Ok(x),
Err(e) => {
let mut e = e.into();
e.i.message.push_str(&f());
e.add_context();
Err(e)
}
}
}
}
impl std::error::Error for PreflateError {}
fn get_io_error_exit_code(e: &std::io::Error) -> ExitCode {
if e.kind() == ErrorKind::UnexpectedEof {
ExitCode::ShortRead
} else {
ExitCode::OsError
}
}
impl From<TryFromIntError> for PreflateError {
#[track_caller]
fn from(e: TryFromIntError) -> Self {
let mut e = PreflateError::new(ExitCode::GeneralFailure, e.to_string().as_str());
e.add_context();
e
}
}
impl From<std::io::Error> for PreflateError {
#[track_caller]
fn from(e: std::io::Error) -> Self {
match e.downcast::<PreflateError>() {
Ok(le) => {
return le;
}
Err(e) => {
let mut e = PreflateError::new(get_io_error_exit_code(&e), e.to_string().as_str());
e.add_context();
e
}
}
}
}
impl From<PreflateError> for std::io::Error {
fn from(e: PreflateError) -> Self {
return std::io::Error::new(std::io::ErrorKind::Other, e);
}
}
#[test]
fn test_error_translation() {
fn my_std_error() -> core::result::Result<(), std::io::Error> {
Err(PreflateError::new(ExitCode::AnalyzeFailed, "test error").into())
}
let e: PreflateError = my_std_error().unwrap_err().into();
assert_eq!(e.exit_code(), ExitCode::AnalyzeFailed);
assert_eq!(e.message(), "test error");
let e: PreflateError =
std::io::Error::new(std::io::ErrorKind::NotFound, "file not found").into();
assert_eq!(e.exit_code(), ExitCode::OsError);
}