use CapturedError as dioxus::Ok() so resources still function as expected (#4851)

This commit is contained in:
Jonathan Kelley authored and GitHub committed 2025-10-27 15:06:21 -07:00
1 parent 9f0bacc85c
commit 281f68faf5
14 files changed
+200 -54

No files matched your search

+8 -7
View File
@@ -1,5 +1,6 @@
#![allow(clippy::needless_return)]
use dioxus_core::CapturedError;
use std::{hint::black_box, prelude::rust_2024::Future, sync::atomic::AtomicBool};
/// `Lazy` is a thread-safe, lazily-initialized global variable.
@@ -14,7 +15,7 @@ use std::{hint::black_box, prelude::rust_2024::Future, sync::atomic::AtomicBool}
pub struct Lazy<T> {
value: std::sync::OnceLock<T>,
started_initialization: AtomicBool,
constructor: Option<fn() -> Result<T, dioxus_core::Error>>,
constructor: Option<fn() -> Result<T, CapturedError>>,
_phantom: std::marker::PhantomData<T>,
}
@@ -36,7 +37,7 @@ impl<T: Send + Sync + 'static> Lazy<T> {
where
F: Fn() -> G + Copy,
G: Future<Output = Result<T, E>> + Send + 'static,
E: Into<dioxus_core::Error>,
E: Into<CapturedError>,
{
if std::mem::size_of::<F>() != 0 {
panic!("The constructor function must be a zero-sized type (ZST). Consider using a function pointer or a closure without captured variables.");
@@ -57,10 +58,10 @@ impl<T: Send + Sync + 'static> Lazy<T> {
///
/// This should only be called once during the server setup phase, typically inside `dioxus::serve`.
/// Future calls to this method will return an error containing the provided value.
pub fn set(&self, pool: T) -> Result<(), dioxus_core::Error> {
pub fn set(&self, pool: T) -> Result<(), CapturedError> {
let res = self.value.set(pool);
if res.is_err() {
return Err(anyhow::anyhow!("Lazy value is already initialized."));
return Err(anyhow::anyhow!("Lazy value is already initialized.").into());
}
Ok(())
@@ -71,7 +72,7 @@ impl<T: Send + Sync + 'static> Lazy<T> {
}
/// Initialize the value of the `Lazy` instance if it hasn't been initialized yet.
pub fn initialize(&self) -> Result<(), dioxus_core::Error> {
pub fn initialize(&self) -> Result<(), CapturedError> {
if let Some(constructor) = self.constructor {
// If we're already initializing this value, wait on the receiver.
if self
@@ -115,12 +116,12 @@ impl<T: Send + Sync + 'static> std::ops::Deref for Lazy<T> {
///
/// We call the `rust-call` method of the zero-sized constructor function. This is safe because we're
/// not actually dereferencing any unsafe data, just calling its vtable entry to get the future.
fn blocking_initialize<T, F, G, E>() -> Result<T, dioxus_core::Error>
fn blocking_initialize<T, F, G, E>() -> Result<T, CapturedError>
where
T: Send + Sync + 'static,
F: Fn() -> G + Copy,
G: Future<Output = Result<T, E>> + Send + 'static,
E: Into<dioxus_core::Error>,
E: Into<CapturedError>,
{
assert_eq!(std::mem::size_of::<F>(), 0, "The constructor function must be a zero-sized type (ZST). Consider using a function pointer or a closure without captured variables.");
+66
View File
@@ -566,6 +566,7 @@ mod resp {
use super::*;
use axum::response::Response;
use dioxus_core::CapturedError;
use http::HeaderValue;
/// A trait for converting the result of the Server Function into an Axum response.
@@ -661,6 +662,71 @@ mod resp {
}
}
impl<T> MakeAxumError<CapturedError> for &&ServerFnDecoder<Result<T, CapturedError>> {
fn make_axum_error(
self,
result: Result<Response, CapturedError>,
) -> Result<Response, Response> {
match result {
Ok(res) => Ok(res),
// Optimize the case where we have sole ownership of the error
Err(errr) if errr._strong_count() == 1 => {
let err = errr.into_inner().unwrap();
<&&ServerFnDecoder<Result<T, anyhow::Error>> as MakeAxumError<anyhow::Error>>::make_axum_error(
&&ServerFnDecoder::new(),
Err(err),
)
}
Err(errr) => {
// The `WithHttpError` trait emits ServerFnErrors so we can downcast them here
// to create richer responses.
let payload = match errr.downcast_ref::<ServerFnError>() {
Some(ServerFnError::ServerError {
message,
code,
details,
}) => ErrorPayload {
message: message.clone(),
code: *code,
data: details.clone(),
},
Some(other) => ErrorPayload {
message: other.to_string(),
code: 500,
data: None,
},
None => match errr.downcast_ref::<HttpError>() {
Some(http_err) => ErrorPayload {
message: http_err
.message
.clone()
.unwrap_or_else(|| http_err.status.to_string()),
code: http_err.status.as_u16(),
data: None,
},
None => ErrorPayload {
code: 500,
message: errr.to_string(),
data: None,
},
},
};
let body = serde_json::to_string(&payload).unwrap();
let mut resp = Response::new(body.into());
resp.headers_mut().insert(
http::header::CONTENT_TYPE,
HeaderValue::from_static("application/json"),
);
*resp.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
Err(resp)
}
}
}
}
impl<T> MakeAxumError<anyhow::Error> for &&ServerFnDecoder<Result<T, anyhow::Error>> {
fn make_axum_error(
self,
+2 -2
View File
@@ -61,7 +61,7 @@ use {
pub fn use_websocket<
In: 'static,
Out: 'static,
E: Into<dioxus_core::Error> + 'static,
E: Into<CapturedError> + 'static,
F: Future<Output = Result<Websocket<In, Out, Enc>, E>> + 'static,
Enc: Encoding,
>(
@@ -72,7 +72,7 @@ pub fn use_websocket<
let status_read = use_hook(|| ReadSignal::new(status));
let connection = use_resource(move || {
let connection = connect_to_websocket().map_err(|e| CapturedError::from(e.into()));
let connection = connect_to_websocket().map_err(|e| e.into());
async move {
let connection = connection.await;
+1 -1
View File
@@ -149,7 +149,7 @@ impl<T, E> std::future::Future for ServerFnRequest<Result<T, E>> {
#[diagnostic::on_unimplemented(
message = "The return type of a server function must be `Result<T, E>`",
note = "`T` is either `impl IntoResponse` *or* `impl Serialize`",
note = "`E` is either `From<ServerFnError> + Serialize`, `dioxus::Error` or `StatusCode`."
note = "`E` is either `From<ServerFnError> + Serialize`, `dioxus::CapturedError` or `StatusCode`."
)]
pub trait AssertIsResult {}
impl<T, E> AssertIsResult for Result<T, E> {}