1use crate::Error;
2use crate::enums::AppMessage;
3use crate::prelude::AppResult;
4use std::borrow::Cow;
5use std::future::Future;
6
7pub trait RecoverAppResultExt<T> {
8 fn recover_from<F>(self, func: F) -> AppResult<T>
9 where
10 F: FnOnce(AppMessage) -> AppResult<T>;
11
12 fn recover_from_async<F, Fut>(self, func: F) -> impl Future<Output = AppResult<T>> + Send
13 where
14 F: FnOnce(AppMessage) -> Fut + Send,
15 Fut: Future<Output = AppResult<T>> + Send;
16}
17
18pub trait AppErrorExt {
19 fn message(&self) -> Cow<'_, str>;
20}
21
22impl<T: Send> RecoverAppResultExt<T> for AppResult<T> {
23 fn recover_from<F>(self, func: F) -> AppResult<T>
24 where
25 F: FnOnce(AppMessage) -> AppResult<T>,
26 {
27 match self {
28 Ok(val) => Ok(val),
29 Err(err) => match err.downcast::<AppMessage>() {
30 Ok(message) => func(message),
31 Err(err) => Err(err),
32 },
33 }
34 }
35
36 async fn recover_from_async<F, Fut>(self, func: F) -> AppResult<T>
37 where
38 F: FnOnce(AppMessage) -> Fut + Send,
39 Fut: Future<Output = AppResult<T>> + Send,
40 {
41 match self {
42 Ok(val) => Ok(val),
43 Err(err) => match err.downcast::<AppMessage>() {
44 Ok(message) => func(message).await,
45 Err(err) => Err(err),
46 },
47 }
48 }
49}
50
51impl<T> RecoverAppResultExt<T> for Error {
52 fn recover_from<F>(self, func: F) -> AppResult<T>
53 where
54 F: FnOnce(AppMessage) -> AppResult<T>,
55 {
56 match self.downcast::<AppMessage>() {
57 Ok(message) => func(message),
58 Err(err) => Err(err),
59 }
60 }
61
62 async fn recover_from_async<F, Fut>(self, func: F) -> AppResult<T>
63 where
64 F: FnOnce(AppMessage) -> Fut + Send,
65 Fut: Future<Output = AppResult<T>> + Send,
66 {
67 match self.downcast::<AppMessage>() {
68 Ok(message) => func(message).await,
69 Err(err) => Err(err),
70 }
71 }
72}
73
74impl AppErrorExt for Error {
75 fn message(&self) -> Cow<'_, str> {
76 match self.downcast_ref::<AppMessage>() {
77 None => Cow::from(self.to_string()),
78 Some(msg) => msg.message(),
79 }
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86 use crate::{internal_server_error, invalid};
87 use http::StatusCode;
88
89 #[test]
90 fn test_recover_from_error() {
91 let result: AppResult<String> = internal_server_error!("Internal Server Error")
92 .recover_from(|err| {
93 assert_eq!(err.status_code(), StatusCode::INTERNAL_SERVER_ERROR);
94 assert_eq!(err.message(), "Internal Server Error");
95 Ok("recovered".to_string())
96 });
97 assert_eq!(result.unwrap(), "recovered");
98 }
99
100 #[test]
101 fn test_recover_from_result() {
102 let result: AppResult<String> = Err(AppMessage::success("User created").into_anyhow())
103 .recover_from(|err| {
104 assert_eq!(err.status_code(), StatusCode::OK);
105 assert_eq!(err.message(), "User created");
106 Ok("recovered".to_string())
107 });
108 assert_eq!(result.unwrap(), "recovered");
109 }
110
111 #[tokio::test]
112 async fn test_recover_from_async_error() {
113 let result: AppResult<String> = internal_server_error!("Internal Server Error")
114 .recover_from_async(|err| {
115 assert_eq!(err.status_code(), StatusCode::INTERNAL_SERVER_ERROR);
116 assert_eq!(err.message(), "Internal Server Error");
117 async { Ok("recovered".to_string()) }
118 })
119 .await;
120 assert_eq!(result.unwrap(), "recovered");
121 }
122
123 #[tokio::test]
124 async fn test_recover_from_async_result() {
125 let result: AppResult<String> = Err(AppMessage::success("User created").into_anyhow())
126 .recover_from_async(|err| {
127 assert_eq!(err.status_code(), StatusCode::OK);
128 assert_eq!(err.message(), "User created");
129 async { Ok("recovered".to_string()) }
130 })
131 .await;
132 assert_eq!(result.unwrap(), "recovered");
133 }
134
135 #[test]
136 fn test_msg() {
137 let err = internal_server_error!("Internal Server Error");
138 let result = err.message();
139 assert_eq!(result, "Internal Server Error");
140
141 let err = invalid!("User has already been suspended");
142 let result = err.message();
143 assert_eq!(result, "User has already been suspended");
144 }
145}