use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BufferError {
PositionOutOfBounds,
IncrementTooLarge,
SizeTooBig,
BufferOverflow,
InvalidState(String),
BufferFull,
BufferEmpty,
InsufficientSpace,
InvalidString,
InvalidData(String),
Io(String),
}
impl fmt::Display for BufferError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::PositionOutOfBounds => write!(f, "Position out of bounds"),
Self::IncrementTooLarge => write!(f, "Increment too large"),
Self::SizeTooBig => write!(f, "Buffer size too big"),
Self::BufferOverflow => write!(f, "Buffer overflow"),
Self::InvalidState(msg) => write!(f, "Invalid state: {}", msg),
Self::BufferFull => write!(f, "Circular buffer is full"),
Self::BufferEmpty => write!(f, "Circular buffer is empty"),
Self::InsufficientSpace => write!(f, "Insufficient space in buffer"),
Self::InvalidString => write!(f, "Invalid string encoding"),
Self::InvalidData(msg) => write!(f, "Invalid data: {}", msg),
Self::Io(msg) => write!(f, "I/O error: {}", msg),
}
}
}
impl std::error::Error for BufferError {}
impl From<BufferError> for std::io::Error {
fn from(err: BufferError) -> Self {
use std::io::ErrorKind;
match err {
BufferError::BufferOverflow | BufferError::InsufficientSpace => {
std::io::Error::new(ErrorKind::WriteZero, err)
}
BufferError::BufferEmpty => std::io::Error::new(ErrorKind::UnexpectedEof, err),
BufferError::Io(msg) => std::io::Error::other(msg),
_ => std::io::Error::other(err),
}
}
}
impl From<std::io::Error> for BufferError {
fn from(err: std::io::Error) -> Self {
BufferError::Io(err.to_string())
}
}
#[cfg(feature = "anyhow")]
impl From<anyhow::Error> for BufferError {
fn from(err: anyhow::Error) -> Self {
BufferError::InvalidState(err.to_string())
}
}
pub type Result<T> = std::result::Result<T, BufferError>;
pub trait ResultExt<T> {
#[cfg(feature = "anyhow")]
fn into_anyhow(self) -> anyhow::Result<T>;
fn into_io(self) -> std::io::Result<T>;
}
impl<T> ResultExt<T> for Result<T> {
#[cfg(feature = "anyhow")]
fn into_anyhow(self) -> anyhow::Result<T> {
self.map_err(|e| e.into())
}
fn into_io(self) -> std::io::Result<T> {
self.map_err(|e| e.into())
}
}
#[macro_export]
macro_rules! buffer_op {
($expr:expr, $target:ty) => {
$expr.map_err(|e: $crate::BufferError| -> $target { e.into() })
};
($expr:expr) => {
$expr.map_err(|e: $crate::BufferError| -> std::io::Error { e.into() })
};
}
#[macro_export]
macro_rules! buffer_try {
($expr:expr) => {
match $expr {
Ok(val) => val,
Err(e) => return Err(e.into()),
}
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_conversion_io() {
let buf_err = BufferError::BufferOverflow;
let io_err: std::io::Error = buf_err.into();
assert_eq!(io_err.kind(), std::io::ErrorKind::WriteZero);
}
#[test]
fn test_result_ext() {
let result: Result<u32> = Ok(42);
let io_result = result.into_io();
assert_eq!(io_result.unwrap(), 42);
}
#[cfg(feature = "anyhow")]
#[test]
fn test_anyhow_conversion() {
let buf_err = BufferError::InvalidString;
let anyhow_err: anyhow::Error = buf_err.into();
assert!(anyhow_err.to_string().contains("Invalid string"));
}
}