use std::error::Error as StdError;
type Location = (&'static str, u32, u32);
pub struct Error {
message: Option<String>,
location: Location,
}
impl Error {
pub fn new<S: Into<String>>(message: Option<S>, location: Location) -> Self {
Error {
message: message.map(|c| c.into()),
location,
}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(msg) = &self.message {
write!(f, "{} ", msg)?;
}
write!(
f,
"\x1b[90m{}:[{}:{}]\x1b[0m",
self.location.0, self.location.1, self.location.2
)
}
}
impl std::fmt::Debug for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self)
}
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
Error::new(Some(err.to_string()), (file!(), line!(), column!()))
}
}
impl From<std::num::ParseIntError> for Error {
fn from(err: std::num::ParseIntError) -> Self {
Error::new(Some(err.to_string()), (file!(), line!(), column!()))
}
}
impl StdError for Error {}
pub type Result<T> = std::result::Result<T, Error>;
#[macro_export]
macro_rules! error {
() => {
$crate::Error::new(
None,
(file!(), line!(), column!()),
)
};
($($arg:tt)*) => {
$crate::Error::new(
Some(format!($($arg)*)),
(file!(), line!(), column!()),
)
};
}
#[macro_export]
macro_rules! bail {
($($arg:tt)*) => {
return Err($crate::error!($($arg)*))
};
}
#[macro_export]
macro_rules! ensure {
($condition:expr, $($arg:tt)*) => {
if !($condition) {
$crate::bail!($($arg)*);
}
};
}
#[cfg(test)]
mod tests {
use super::*;
fn example_function(value: i32) -> Result<()> {
ensure!(value >= 0, "value must be non-negative");
if value > 100 {
bail!("bigger than x");
}
Ok(())
}
#[test]
fn test_error_creation() {
let err: Result<()> = Err(error!("test error"));
assert!(err.is_err_and(|e| e.to_string().contains("test error")));
}
#[test]
fn test_error_formatted() {
let err: Result<()> = Err(error!("test error {}, {} {:?}", "formatted", 1, vec![2, 3]));
assert!(err.is_err_and(|e| e.to_string().contains("[2, 3]")));
}
#[test]
fn test_example_function() {
assert!(example_function(-1).is_err());
assert!(example_function(50).is_ok());
assert!(example_function(101).is_err());
}
}