1#[derive(Debug, thiserror::Error, PartialEq)]
2pub enum SpecialError {
3 #[error("single error {0}: {1}")]
4 Single(String, String),
5 #[error("multi error {0:?}")]
6 Multi(ft_sdk::FormError),
7 #[error("not found: {0}")]
8 NotFound(String),
9 #[error("server error: {0}")]
10 ServerError(String),
11 #[error("unauthorised: {0}")]
12 Unauthorised(String),
13}
14
15#[macro_export]
17macro_rules! server_error {
18 ($($t:tt)*) => {{
19 let msg = format!($($t)*);
20 $crate::server_error_(msg)
21 }};
22}
23
24#[doc(hidden)]
25pub fn server_error_(msg: String) -> SpecialError {
26 SpecialError::ServerError(msg)
27}
28
29#[macro_export]
31macro_rules! not_found {
32 ($($t:tt)*) => {{
33 let msg = format!($($t)*);
34 $crate::not_found_(msg)
35 }};
36}
37
38#[doc(hidden)]
39pub fn not_found_(msg: String) -> SpecialError {
40 SpecialError::NotFound(msg)
41}
42
43#[macro_export]
45macro_rules! unauthorised {
46 ($($t:tt)*) => {{
47 let msg = format!($($t)*);
48 $crate::unauthorised_(msg)
49 }};
50}
51
52#[doc(hidden)]
53pub fn unauthorised_(msg: String) -> SpecialError {
54 SpecialError::Unauthorised(msg)
55}
56
57pub fn single_error<K: AsRef<str>, E: AsRef<str>>(k: K, e: E) -> SpecialError {
58 SpecialError::Single(k.as_ref().to_string(), e.as_ref().to_string())
59}
60
61fn je(r: Result<http::Response<bytes::Bytes>, ft_sdk::Error>) -> http::Response<bytes::Bytes> {
62 r.unwrap_or_else(|e| {
63 http::Response::builder()
64 .status(http::StatusCode::INTERNAL_SERVER_ERROR)
65 .body(format!("json error: {e:?}\n").into())
66 .unwrap()
67 })
68}
69
70pub fn handle_error(e: anyhow::Error) -> http::Response<bytes::Bytes> {
71 if let Some(field_error) = e.downcast_ref::<SpecialError>() {
72 ft_sdk::println!("special error: {field_error}");
73 return match field_error {
74 SpecialError::Single(k, se) => je(crate::json(serde_json::json!({"errors": {k: se}}))),
75 SpecialError::Multi(me) => je(crate::json(serde_json::json!({"errors": me}))),
76 SpecialError::NotFound(msg) => http::Response::builder()
77 .status(http::StatusCode::NOT_FOUND)
78 .body(format!("page not found: {msg}\n").into())
79 .unwrap(),
80 SpecialError::Unauthorised(msg) => http::Response::builder()
81 .status(http::StatusCode::UNAUTHORIZED)
82 .body(format!("unauthorised: {msg}\n").into())
83 .unwrap(),
84 SpecialError::ServerError(msg) => http::Response::builder()
85 .status(http::StatusCode::INTERNAL_SERVER_ERROR)
86 .body(format!("server error: {msg}\n").into())
87 .unwrap(),
88 };
89 }
90 http::Response::builder()
91 .status(http::StatusCode::INTERNAL_SERVER_ERROR)
92 .body(format!("json error: {e:?}\n").into())
93 .unwrap()
94}
95
96#[cfg(test)]
97mod test {
98 use anyhow::Context;
99
100 #[derive(thiserror::Error, Debug)]
101 enum EFirst {
102 #[error("yo")]
103 Yo,
104 }
105
106 fn outer() -> Result<(), anyhow::Error> {
107 anyhow::Ok(out()?).context(http::StatusCode::CREATED)
108 }
109
110 fn out() -> Result<(), anyhow::Error> {
111 anyhow::Ok(first()?).context(http::StatusCode::ACCEPTED)
112 }
113
114 fn first() -> Result<(), anyhow::Error> {
115 Err(EFirst::Yo).context(http::StatusCode::SEE_OTHER)
116 }
117
118 #[test]
119 fn t() {
120 let e = outer().unwrap_err();
121 assert_eq!(
122 *e.downcast_ref::<http::StatusCode>().unwrap(),
123 http::StatusCode::SEE_OTHER
124 );
125 }
129
130 #[derive(thiserror::Error, Debug, PartialEq)]
131 enum Status {
132 #[error("created")]
133 Created,
134 #[error("accepted")]
135 Accepted,
136 #[error("see-other")]
137 SeeOther,
138 }
139
140 fn outer2() -> Result<(), anyhow::Error> {
141 anyhow::Ok(out2()?).context(Status::Created)
142 }
143
144 fn out2() -> Result<(), anyhow::Error> {
145 anyhow::Ok(first2()?).context(Status::Accepted)
146 }
147
148 fn first2() -> Result<(), anyhow::Error> {
149 Err(EFirst::Yo).context(Status::SeeOther)
150 }
151
152 #[test]
153 fn t2() {
154 let e = outer2().unwrap_err();
155 println!("status1: {:?}", e.downcast_ref::<Status>());
156 for cause in e.chain() {
157 println!("status: {:?}", cause.downcast_ref::<Status>());
158 }
159
160 }
165
166 fn outer3() -> Result<(), anyhow::Error> {
167 Ok(do_something()?)
168 }
169
170 #[derive(thiserror::Error, Debug)]
171 enum DoSomethingError {
172 #[error("get user error {0}")]
173 GetUser(#[from] GetUserError),
175 }
176
177 fn do_something() -> Result<(), DoSomethingError> {
178 get_user()?;
179 Ok(())
180 }
181
182 #[derive(thiserror::Error, Debug)]
183 enum GetUserError {
184 #[error("unauthorised {0}")]
185 Unauthorised(#[from] super::SpecialError),
187 }
188
189 fn get_user() -> Result<i32, GetUserError> {
190 Err(GetUserError::Unauthorised(
191 super::SpecialError::Unauthorised("yo".to_string()),
192 ))
193 }
194
195 #[test]
196 fn t3() {
197 let e = outer3().unwrap_err();
198
199 assert_eq!(e.downcast_ref::<super::SpecialError>(), None);
200 let mut iter = e.chain();
201 assert_eq!(
202 iter.next().unwrap().downcast_ref::<super::SpecialError>(),
203 None
204 );
205 assert_eq!(
206 iter.next().unwrap().downcast_ref::<super::SpecialError>(),
207 None
208 );
209 assert_eq!(
210 iter.next().unwrap().downcast_ref::<super::SpecialError>(),
211 Some(super::SpecialError::Unauthorised("yo".to_string())).as_ref()
212 );
213 assert!(iter.next().is_none());
214
215 }
218}