use crate::Error;
use crate::enums::AppMessage;
use crate::prelude::AppResult;
use std::borrow::Cow;
use std::future::Future;
pub trait RecoverAppResultExt<T> {
fn recover_from<F>(self, func: F) -> AppResult<T>
where
F: FnOnce(AppMessage) -> AppResult<T>;
fn recover_from_async<F, Fut>(self, func: F) -> impl Future<Output = AppResult<T>> + Send
where
F: FnOnce(AppMessage) -> Fut + Send,
Fut: Future<Output = AppResult<T>> + Send;
}
pub trait AppErrorExt {
fn message(&self) -> Cow<'_, str>;
}
impl<T: Send> RecoverAppResultExt<T> for AppResult<T> {
fn recover_from<F>(self, func: F) -> AppResult<T>
where
F: FnOnce(AppMessage) -> AppResult<T>,
{
match self {
Ok(val) => Ok(val),
Err(err) => match err.downcast::<AppMessage>() {
Ok(message) => func(message),
Err(err) => Err(err),
},
}
}
async fn recover_from_async<F, Fut>(self, func: F) -> AppResult<T>
where
F: FnOnce(AppMessage) -> Fut + Send,
Fut: Future<Output = AppResult<T>> + Send,
{
match self {
Ok(val) => Ok(val),
Err(err) => match err.downcast::<AppMessage>() {
Ok(message) => func(message).await,
Err(err) => Err(err),
},
}
}
}
impl<T> RecoverAppResultExt<T> for Error {
fn recover_from<F>(self, func: F) -> AppResult<T>
where
F: FnOnce(AppMessage) -> AppResult<T>,
{
match self.downcast::<AppMessage>() {
Ok(message) => func(message),
Err(err) => Err(err),
}
}
async fn recover_from_async<F, Fut>(self, func: F) -> AppResult<T>
where
F: FnOnce(AppMessage) -> Fut + Send,
Fut: Future<Output = AppResult<T>> + Send,
{
match self.downcast::<AppMessage>() {
Ok(message) => func(message).await,
Err(err) => Err(err),
}
}
}
impl AppErrorExt for Error {
fn message(&self) -> Cow<'_, str> {
match self.downcast_ref::<AppMessage>() {
None => Cow::from(self.to_string()),
Some(msg) => msg.message(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{internal_server_error, invalid};
use http::StatusCode;
#[test]
fn test_recover_from_error() {
let result: AppResult<String> = internal_server_error!("Internal Server Error")
.recover_from(|err| {
assert_eq!(err.status_code(), StatusCode::INTERNAL_SERVER_ERROR);
assert_eq!(err.message(), "Internal Server Error");
Ok("recovered".to_string())
});
assert_eq!(result.unwrap(), "recovered");
}
#[test]
fn test_recover_from_result() {
let result: AppResult<String> = Err(AppMessage::success("User created").into_anyhow())
.recover_from(|err| {
assert_eq!(err.status_code(), StatusCode::OK);
assert_eq!(err.message(), "User created");
Ok("recovered".to_string())
});
assert_eq!(result.unwrap(), "recovered");
}
#[tokio::test]
async fn test_recover_from_async_error() {
let result: AppResult<String> = internal_server_error!("Internal Server Error")
.recover_from_async(|err| {
assert_eq!(err.status_code(), StatusCode::INTERNAL_SERVER_ERROR);
assert_eq!(err.message(), "Internal Server Error");
async { Ok("recovered".to_string()) }
})
.await;
assert_eq!(result.unwrap(), "recovered");
}
#[tokio::test]
async fn test_recover_from_async_result() {
let result: AppResult<String> = Err(AppMessage::success("User created").into_anyhow())
.recover_from_async(|err| {
assert_eq!(err.status_code(), StatusCode::OK);
assert_eq!(err.message(), "User created");
async { Ok("recovered".to_string()) }
})
.await;
assert_eq!(result.unwrap(), "recovered");
}
#[test]
fn test_msg() {
let err = internal_server_error!("Internal Server Error");
let result = err.message();
assert_eq!(result, "Internal Server Error");
let err = invalid!("User has already been suspended");
let result = err.message();
assert_eq!(result, "User has already been suspended");
}
}