use core::fmt::{self, Debug, Display, Formatter};
pub type Result<T> = core::result::Result<T, Error>;
pub struct Error {
pub(crate) kind: ErrorKind,
}
impl Error {
#[doc(hidden)]
#[track_caller]
pub const fn panic(self) -> ! {
panic!("{}", self.kind.description());
}
}
#[derive(Debug, Copy, Clone)]
#[non_exhaustive]
pub enum ErrorKind {
OutOfRange,
ParseTrailing,
ParseInvalid,
ParseNotEnough,
TimeZoneFile,
}
impl ErrorKind {
pub const fn description(self) -> &'static str {
match self {
Self::OutOfRange => "component out of permitted range",
Self::ParseTrailing => "unexpected trailing characters in input while parsing",
Self::ParseInvalid => "input contains invalid characters while parsing",
Self::ParseNotEnough => "input prematurely ended while parsing",
Self::TimeZoneFile => "malformed time-zone interchange file (TZif)",
}
}
}
impl Debug for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.kind.fmt(f)
}
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.pad(self.kind.description())
}
}
impl core::error::Error for Error {}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Self {
Self { kind }
}
}
macro_rules! ensure_range {
($min:expr, $max:expr, $value:expr) => {
#[allow(unused_comparisons)]
if $value > $max || $value < $min {
return Err($crate::Error {
kind: $crate::error::ErrorKind::OutOfRange,
});
}
};
}
#[allow(unused)]
pub trait ResultExt<T>: Sized {
fn context(self, kind: ErrorKind) -> Result<T>;
fn context_tzif(self) -> Result<T> {
self.context(ErrorKind::TimeZoneFile)
}
}
impl<T, E: core::error::Error> ResultExt<T> for core::result::Result<T, E> {
fn context(self, kind: ErrorKind) -> Result<T> {
self.map_err(|error| {
log::error!("{}: {}", kind.description(), error);
kind.into()
})
}
}