hexga_io 0.0.11-beta.53

io file abstraction based on serde to allow loading/saving/converting bytes with a custom extension
Documentation
use super::*;

pub type IoResult<T = ()> = Result<T, IoError>;

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum IoMode
{
    Read,
    Write,
}

impl Display for IoMode
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
    {
        match self
        {
            IoMode::Read => f.write_str("Read"),
            IoMode::Write => f.write_str("Write"),
        }
    }
}

impl IoMode
{
    pub const fn is_read(self) -> bool { matches!(self, Self::Read) }
    pub const fn is_write(self) -> bool { matches!(self, Self::Write) }
}

#[derive(Default, Clone, PartialEq, Eq)]
pub struct IoError
{
    pub path: PathBuf,
    pub mode: Option<IoMode>,
    pub kind: FileError,
}
impl IoError
{
    pub fn new(path: impl Into<PathBuf>, kind: impl Into<FileError>) -> Self
    {
        Self {
            path: path.into(),
            kind: kind.into(),
            mode: None,
        }
    }
    pub fn with_mode(self, mode: Option<IoMode>) -> Self { Self { mode, ..self } }
    pub fn when_reading(self) -> Self
    {
        Self {
            mode: Some(IoMode::Read),
            ..self
        }
    }
    pub fn when_writing(self) -> Self
    {
        Self {
            mode: Some(IoMode::Write),
            ..self
        }
    }
}
impl Display for IoError
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
    {
        write!(f, "IO error at {:?} ", self.path)?;
        if let Some(mode) = self.mode
        {
            match mode
            {
                IoMode::Read => write!(f, "when reading")?,
                IoMode::Write => write!(f, "when writing")?,
            }
        }
        write!(f, ": {}", self.kind)
    }
}
impl std::fmt::Debug for IoError
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self) }
}

impl std::error::Error for IoError {}

#[cfg(feature = "serde")]
impl serde::ser::Error for IoError
{
    fn custom<T>(msg: T) -> Self
    where
        T: std::fmt::Display,
    {
        Self {
            kind: FileError::custom(msg.to_string()),
            ..Default::default()
        }
    }
}
#[cfg(feature = "serde")]
impl serde::de::Error for IoError
{
    fn custom<T>(msg: T) -> Self
    where
        T: std::fmt::Display,
    {
        Self {
            kind: FileError::custom(msg.to_string()),
            ..Default::default()
        }
    }
}

pub type FileResult<T = ()> = Result<T, FileError>;

#[non_exhaustive]
#[derive(Default, Clone, PartialEq, Eq)]
pub enum FileError
{
    #[default]
    Unknow,
    NotSupported,
    DownloadFailed,
    Unimplemented,
    NotFound,
    Custom(Reason),
    Std(std::io::ErrorKind),
    Encoding(EncodeError),
}
impl From<EncodeError> for FileError
{
    fn from(value: EncodeError) -> Self { Self::Encoding(value) }
}
impl From<String> for FileError
{
    fn from(custom: String) -> Self { Self::Custom(custom.into()) }
}
impl From<&'static str> for FileError
{
    fn from(custom: &'static str) -> Self { Self::Custom(custom.into()) }
}

impl From<FromUtf8Error> for FileError
{
    fn from(value: FromUtf8Error) -> Self { value.utf8_error().into() }
}
impl From<Utf8Error> for FileError
{
    fn from(value: Utf8Error) -> Self { FileError::Encoding(value.into()) }
}
impl From<Base64Error> for FileError
{
    fn from(value: Base64Error) -> Self { FileError::Encoding(value.into()) }
}
impl From<std::io::Error> for FileError
{
    fn from(value: std::io::Error) -> Self { value.kind().into() }
}
impl From<std::io::ErrorKind> for FileError
{
    fn from(kind: std::io::ErrorKind) -> Self { Self::Std(kind) }
}
impl From<std::fmt::Error> for FileError
{
    fn from(value: std::fmt::Error) -> Self { Self::Encoding(value.into()) }
}

impl FileError
{
    pub fn custom(reason: impl Into<Reason>) -> Self { Self::Custom(reason.into()) }
    pub fn from_display(reason: impl Display) -> Self { Self::custom(reason.to_string()) }

    pub fn is_encoding(&self) -> bool { matches!(self, Self::Encoding(_)) }
}

impl Display for FileError
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
    {
        match self
        {
            FileError::Unknow => f.write_str("unknow"),
            FileError::Unimplemented => f.write_str("unimplemented"),
            FileError::NotSupported => f.write_str("not supported"),
            FileError::DownloadFailed => f.write_str("download failed"),
            FileError::NotFound => f.write_str("not found"),
            FileError::Custom(reason) => write!(f, "custom: {reason}"),
            FileError::Std(kind) => write!(f, "std: {kind}"),
            FileError::Encoding(encode_error) => write!(f, "encoding: {encode_error}"),
        }
    }
}
impl std::fmt::Debug for FileError
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self) }
}

impl std::error::Error for FileError {}
#[cfg(feature = "serde")]
impl serde::ser::Error for FileError
{
    fn custom<T>(msg: T) -> Self
    where
        T: std::fmt::Display,
    {
        Self::custom(msg.to_string())
    }
}
#[cfg(feature = "serde")]
impl serde::de::Error for FileError
{
    fn custom<T>(msg: T) -> Self
    where
        T: std::fmt::Display,
    {
        Self::custom(msg.to_string())
    }
}