dioxus_fullstack_core/
error.rs1use axum_core::response::IntoResponse;
2use futures_util::TryStreamExt;
3use http::StatusCode;
4use serde::{Deserialize, Serialize};
5use std::fmt::Debug;
6
7use crate::HttpError;
8
9pub type ServerFnResult<T = ()> = std::result::Result<T, ServerFnError>;
23
24#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
27pub enum ServerFnError {
28 #[error("error running server function: {message} (details: {details:#?})")]
34 ServerError {
35 message: String,
37
38 code: u16,
40
41 #[serde(skip_serializing_if = "Option::is_none")]
42 details: Option<serde_json::Value>,
43 },
44
45 #[error("error reaching server to call server function: {0} ")]
47 Request(RequestError),
48
49 #[error("error reading response body stream: {0}")]
51 StreamError(String),
52
53 #[error("error while trying to register the server function: {0}")]
55 Registration(String),
56
57 #[error("error trying to build `HTTP` method request: {0}")]
59 UnsupportedRequestMethod(String),
60
61 #[error("error running middleware: {0}")]
63 MiddlewareError(String),
64
65 #[error("error deserializing server function results: {0}")]
67 Deserialization(String),
68
69 #[error("error serializing server function arguments: {0}")]
71 Serialization(String),
72
73 #[error("error deserializing server function arguments: {0}")]
75 Args(String),
76
77 #[error("missing argument {0}")]
79 MissingArg(String),
80
81 #[error("error creating response {0}")]
83 Response(String),
84}
85
86impl ServerFnError {
87 pub fn new(f: impl ToString) -> Self {
89 ServerFnError::ServerError {
90 message: f.to_string(),
91 details: None,
92 code: 500,
93 }
94 }
95
96 pub async fn from_axum_response(resp: axum_core::response::Response) -> Self {
98 let status = resp.status();
99 let message = resp
100 .into_body()
101 .into_data_stream()
102 .try_fold(Vec::new(), |mut acc, chunk| async move {
103 acc.extend_from_slice(&chunk);
104 Ok(acc)
105 })
106 .await
107 .ok()
108 .and_then(|bytes| String::from_utf8(bytes).ok())
109 .unwrap_or_else(|| status.canonical_reason().unwrap_or("").to_string());
110
111 ServerFnError::ServerError {
112 message,
113 code: status.as_u16(),
114 details: None,
115 }
116 }
117}
118
119impl From<anyhow::Error> for ServerFnError {
120 fn from(value: anyhow::Error) -> Self {
121 ServerFnError::ServerError {
122 message: value.to_string(),
123 details: None,
124 code: 500,
125 }
126 }
127}
128
129impl From<serde_json::Error> for ServerFnError {
130 fn from(value: serde_json::Error) -> Self {
131 ServerFnError::Deserialization(value.to_string())
132 }
133}
134
135impl From<ServerFnError> for http::StatusCode {
136 fn from(value: ServerFnError) -> Self {
137 match value {
138 ServerFnError::ServerError { code, .. } => {
139 http::StatusCode::from_u16(code).unwrap_or(http::StatusCode::INTERNAL_SERVER_ERROR)
140 }
141 ServerFnError::Request(err) => match err {
142 RequestError::Status(_, code) => http::StatusCode::from_u16(code)
143 .unwrap_or(http::StatusCode::INTERNAL_SERVER_ERROR),
144 _ => http::StatusCode::INTERNAL_SERVER_ERROR,
145 },
146 ServerFnError::StreamError(_)
147 | ServerFnError::Registration(_)
148 | ServerFnError::UnsupportedRequestMethod(_)
149 | ServerFnError::MiddlewareError(_)
150 | ServerFnError::Deserialization(_)
151 | ServerFnError::Serialization(_)
152 | ServerFnError::Args(_)
153 | ServerFnError::MissingArg(_)
154 | ServerFnError::Response(_) => http::StatusCode::INTERNAL_SERVER_ERROR,
155 }
156 }
157}
158
159impl From<RequestError> for ServerFnError {
160 fn from(value: RequestError) -> Self {
161 ServerFnError::Request(value)
162 }
163}
164
165impl From<ServerFnError> for HttpError {
166 fn from(value: ServerFnError) -> Self {
167 let status = StatusCode::from_u16(match &value {
168 ServerFnError::ServerError { code, .. } => *code,
169 _ => 500,
170 })
171 .unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
172 HttpError {
173 status,
174 message: Some(value.to_string()),
175 }
176 }
177}
178
179impl From<HttpError> for ServerFnError {
180 fn from(value: HttpError) -> Self {
181 ServerFnError::ServerError {
182 message: value.message.unwrap_or_else(|| {
183 value
184 .status
185 .canonical_reason()
186 .unwrap_or("Unknown error")
187 .to_string()
188 }),
189 code: value.status.as_u16(),
190 details: None,
191 }
192 }
193}
194
195impl IntoResponse for ServerFnError {
196 fn into_response(self) -> axum_core::response::Response {
197 match self {
198 Self::ServerError {
199 message,
200 code,
201 details,
202 } => {
203 let status =
204 StatusCode::from_u16(code).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
205 let body = if let Some(details) = details {
206 serde_json::json!({
207 "error": message,
208 "details": details,
209 })
210 } else {
211 serde_json::json!({
212 "error": message,
213 })
214 };
215 let body = axum_core::body::Body::from(
216 serde_json::to_string(&body)
217 .unwrap_or_else(|_| "{\"error\":\"Internal Server Error\"}".to_string()),
218 );
219 axum_core::response::Response::builder()
220 .status(status)
221 .header("Content-Type", "application/json")
222 .body(body)
223 .unwrap_or_else(|_| {
224 axum_core::response::Response::builder()
225 .status(StatusCode::INTERNAL_SERVER_ERROR)
226 .body(axum_core::body::Body::from(
227 "{\"error\":\"Internal Server Error\"}",
228 ))
229 .unwrap()
230 })
231 }
232 _ => {
233 let status: StatusCode = self.clone().into();
234 let body = axum_core::body::Body::from(
235 serde_json::json!({
236 "error": self.to_string(),
237 })
238 .to_string(),
239 );
240 axum_core::response::Response::builder()
241 .status(status)
242 .header("Content-Type", "application/json")
243 .body(body)
244 .unwrap_or_else(|_| {
245 axum_core::response::Response::builder()
246 .status(StatusCode::INTERNAL_SERVER_ERROR)
247 .body(axum_core::body::Body::from(
248 "{\"error\":\"Internal Server Error\"}",
249 ))
250 .unwrap()
251 })
252 }
253 }
254 }
255}
256
257#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
262pub enum RequestError {
263 #[error("error building request: {0}")]
265 Builder(String),
266
267 #[error("error serializing request body: {0}")]
269 Serialization(String),
270
271 #[error("error following redirect: {0}")]
273 Redirect(String),
274
275 #[error("error receiving status code: {0} ({1})")]
277 Status(String, u16),
278
279 #[error("error timing out: {0}")]
281 Timeout(String),
282
283 #[error("error sending request: {0}")]
285 Request(String),
286
287 #[error("error upgrading connection: {0}")]
289 Connect(String),
290
291 #[error("request or response body error: {0}")]
293 Body(String),
294
295 #[error("error decoding response body: {0}")]
297 Decode(String),
298}
299
300impl RequestError {
301 pub fn status(&self) -> Option<StatusCode> {
302 match self {
303 RequestError::Status(_, code) => Some(StatusCode::from_u16(*code).ok()?),
304 _ => None,
305 }
306 }
307
308 pub fn status_code(&self) -> Option<u16> {
309 match self {
310 RequestError::Status(_, code) => Some(*code),
311 _ => None,
312 }
313 }
314}