use super::cmd::{Format, ResetStyle, SetForeground8};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ErrorKind {
NoData,
InFlight,
MalformedUtf8,
MalformedSequence,
PathologicalSequence,
BadControl,
BadSequence,
NotASequence,
OutOfMemory,
TooFewCoordinates,
TooManyCoordinates,
EmptyCoordinate,
OversizedCoordinate,
MalformedCoordinate,
Unreadable,
}
impl ErrorKind {
pub fn as_str(&self) -> &'static str {
match *self {
Self::NoData => "reading terminal input timed out without returning data",
Self::InFlight => "token is in-flight, hence access to raw bytes is not safe",
Self::MalformedUtf8 => "malformed UTF-8",
Self::MalformedSequence => "malformed ANSI escape sequence",
Self::PathologicalSequence => "pathologically long ANSI escape sequence",
Self::BadControl => "unexpected control for ANSI escape sequence",
Self::BadSequence => "unexpected ANSI escape sequence",
Self::NotASequence => "token not an ANSI escape sequence",
Self::OutOfMemory => "ANSI escape sequence too long for internal buffer",
Self::Unreadable => "error reading terminal",
Self::TooFewCoordinates => "too few color coordinates",
Self::TooManyCoordinates => "too many color coordinates",
Self::EmptyCoordinate => "empty color coordinate",
Self::OversizedCoordinate => "oversized color coordinate",
Self::MalformedCoordinate => "malformed color coordinate",
}
}
}
impl From<ErrorKind> for std::io::Error {
fn from(value: ErrorKind) -> Self {
Error::from(value).into()
}
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Self {
Self { kind, source: None }
}
}
#[derive(Debug)]
pub struct Error {
kind: ErrorKind,
source: Option<std::io::Error>,
}
impl Error {
#[must_use = "the only reason to invoke method is to access the returned value"]
pub fn unreadable(source: std::io::Error) -> Self {
Self {
kind: ErrorKind::Unreadable,
source: Some(source),
}
}
pub fn kind(&self) -> ErrorKind {
self.kind
}
}
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(self.kind.as_str())
}
}
impl core::error::Error for Error {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
if let &Self {
kind: ErrorKind::Unreadable,
source: Some(ref error),
} = self
{
Some(error)
} else {
None
}
}
}
impl From<std::io::Error> for Error {
fn from(value: std::io::Error) -> Self {
Self::unreadable(value)
}
}
impl From<Error> for std::io::Error {
fn from(value: Error) -> Self {
use self::ErrorKind::*;
match value.kind {
MalformedUtf8 | MalformedSequence | PathologicalSequence | BadControl | BadSequence
| NotASequence | TooFewCoordinates | TooManyCoordinates | EmptyCoordinate
| OversizedCoordinate | MalformedCoordinate => {
Self::new(std::io::ErrorKind::InvalidData, value)
}
NoData => std::io::ErrorKind::TimedOut.into(),
InFlight => std::io::ErrorKind::ResourceBusy.into(),
OutOfMemory => std::io::ErrorKind::OutOfMemory.into(),
Unreadable =>
{
#[allow(clippy::option_if_let_else)]
if let Some(error) = value.source {
error
} else {
Self::new(std::io::ErrorKind::Other, value)
}
}
}
}
}
pub fn should_retry<T, E>(result: core::result::Result<T, E>) -> bool
where
E: Into<std::io::Error>,
{
if let Err(err) = result {
let kind = err.into().kind();
kind == std::io::ErrorKind::Interrupted || kind == std::io::ErrorKind::TimedOut
} else {
false
}
}
#[allow(clippy::print_stdout)]
pub fn report<E: core::error::Error>(error: &E) {
println!(
"{}{}ERROR: {}{}",
Format::Bold,
SetForeground8::<1>,
error,
ResetStyle
);
let mut error: &dyn core::error::Error = error;
while let Some(inner) = error.source() {
println!(" {}", inner);
error = inner;
}
}