use CapturedError as dioxus::Ok() so resources still function as expected (#4851)
This commit is contained in:
1 parent
9f0bacc85c
commit
281f68faf5
14 files changed
+200
-54
No files matched your search
@@ -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.");
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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> {}
|
||||
|
||||
Reference in new issue
Block a user