errify 0.3.0

Function error context provider
Documentation
mod utils;

use std::{fmt::Display, ops::Deref};

use errify::errify_with;
use utils::*;

#[test]
fn simple_closure() {
    #[errify_with(|| format!("closure {arg}"))]
    fn func(arg: i32) -> Result<i32, ErrorWithContext> {
        Err(ErrorWithContext::new(arg))
    }

    let err = func(1).unwrap_err();
    assert_eq!(err.msg.deref(), "1");
    assert_eq!(err.cx.as_deref(), Some("closure 1"));
}

#[test]
fn simple_fn() {
    fn context() -> impl Display {
        ContextExpr::new(2)
    }

    #[errify_with(context)]
    fn func(arg: i32) -> Result<i32, ErrorWithContext> {
        Err(ErrorWithContext::new(arg))
    }

    let err = func(1).unwrap_err();
    assert_eq!(err.msg.deref(), "1");
    assert_eq!(err.cx.as_deref(), Some("ContextExpr(2)"));
}

#[tokio::test]
async fn async_closure() {
    #[errify_with(|| format!("closure {arg}"))]
    async fn func(arg: i32) -> Result<i32, ErrorWithContext> {
        Err(ErrorWithContext::new(arg))
    }

    let err = func(1).await.unwrap_err();
    assert_eq!(err.msg.deref(), "1");
    assert_eq!(err.cx.as_deref(), Some("closure 1"));
}

#[tokio::test]
async fn async_fn() {
    fn context() -> impl Display {
        ContextExpr::new(2)
    }

    #[errify_with(context)]
    async fn func(arg: i32) -> Result<i32, ErrorWithContext> {
        Err(ErrorWithContext::new(arg))
    }

    let err = func(1).await.unwrap_err();
    assert_eq!(err.msg.deref(), "1");
    assert_eq!(err.cx.as_deref(), Some("ContextExpr(2)"));
}

#[test]
fn unsafe_closure() {
    #[errify_with(|| format!("closure {arg}"))]
    unsafe fn func(arg: i32) -> Result<i32, ErrorWithContext> {
        Err(ErrorWithContext::new(arg))
    }

    let err = unsafe { func(1).unwrap_err() };
    assert_eq!(err.msg.deref(), "1");
    assert_eq!(err.cx.as_deref(), Some("closure 1"));
}

#[test]
fn unsafe_fn() {
    fn context() -> impl Display {
        ContextExpr::new(2)
    }

    #[errify_with(context)]
    unsafe fn func(arg: i32) -> Result<i32, ErrorWithContext> {
        Err(ErrorWithContext::new(arg))
    }

    let err = unsafe { func(1).unwrap_err() };
    assert_eq!(err.msg.deref(), "1");
    assert_eq!(err.cx.as_deref(), Some("ContextExpr(2)"));
}

#[tokio::test]
async fn async_unsafe_closure() {
    #[errify_with(|| format!("closure {arg}"))]
    async unsafe fn func(arg: i32) -> Result<i32, ErrorWithContext> {
        Err(ErrorWithContext::new(arg))
    }

    let err = unsafe { func(1).await.unwrap_err() };
    assert_eq!(err.msg.deref(), "1");
    assert_eq!(err.cx.as_deref(), Some("closure 1"));
}

#[tokio::test]
async fn async_unsafe_fn() {
    fn context() -> impl Display {
        ContextExpr::new(2)
    }

    #[errify_with(context)]
    async unsafe fn func(arg: i32) -> Result<i32, ErrorWithContext> {
        Err(ErrorWithContext::new(arg))
    }

    let err = unsafe { func(1).await.unwrap_err() };
    assert_eq!(err.msg.deref(), "1");
    assert_eq!(err.cx.as_deref(), Some("ContextExpr(2)"));
}

#[test]
fn method() {
    #[derive(Debug)]
    struct Struct;

    impl Struct {
        #[errify_with(|| format!("closure self = {self:?}"))]
        fn func(&self, arg: String) -> Result<i32, ErrorWithContext> {
            Err(ErrorWithContext::new(arg))
        }
    }

    let err = Struct.func("argument".to_owned()).unwrap_err();
    assert_eq!(err.msg.deref(), "argument");
    assert_eq!(err.cx.as_deref(), Some("closure self = Struct"));
}

#[test]
fn trait_method() {
    trait Trait {
        fn func(&self, arg: String) -> Result<i32, ErrorWithContext>;
    }

    #[derive(Debug)]
    struct Struct;

    impl Trait for Struct {
        #[errify_with(|| format!("closure self = {self:?}"))]
        fn func(&self, arg: String) -> Result<i32, ErrorWithContext> {
            Err(ErrorWithContext::new(arg))
        }
    }

    let err = Trait::func(&Struct, "argument".to_owned()).unwrap_err();
    assert_eq!(err.msg.deref(), "argument");
    assert_eq!(err.cx.as_deref(), Some("closure self = Struct"));
}

#[test]
fn check_visibility() {
    pub mod multiple {
        use super::*;
        pub mod module {
            use super::*;
            #[errify_with(|| format!("closure {arg}"))]
            pub fn func(arg: i32) -> Result<i32, ErrorWithContext> {
                Err(ErrorWithContext::new(arg))
            }
        }
    }

    let err = multiple::module::func(1).unwrap_err();
    assert_eq!(err.msg.deref(), "1");
    assert_eq!(err.cx.as_deref(), Some("closure 1"));
}

#[cfg(feature = "anyhow")]
#[test]
fn anyhow_error() {
    #[errify_with(|| format!("closure {arg} = {}", arg))]
    fn func(arg: i32) -> Result<i32, anyhow::Error> {
        Err(anyhow::anyhow!("error {}", arg))
    }

    let err = func(1).unwrap_err();
    let context_err = err.to_string();
    let custom_err = err.root_cause().to_string();
    assert_eq!(context_err, "closure 1 = 1");
    assert_eq!(custom_err, "error 1");
}

#[cfg(feature = "eyre")]
#[test]
fn eyre_error() {
    #[errify_with(|| format!("closure {arg} = {}", arg))]
    fn func(arg: i32) -> Result<i32, eyre::Report> {
        Err(eyre::eyre!("error {}", arg))
    }

    let err = func(1).unwrap_err();
    let context_err = err.to_string();
    let custom_err = err.root_cause().to_string();
    assert_eq!(context_err, "closure 1 = 1");
    assert_eq!(custom_err, "error 1");
}