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
@@ -42,7 +42,7 @@ pub fn NavBar() -> Element {
|
|||||||
#[component]
|
#[component]
|
||||||
pub fn DogView() -> Element {
|
pub fn DogView() -> Element {
|
||||||
let mut img_src = use_loader(|| async move {
|
let mut img_src = use_loader(|| async move {
|
||||||
anyhow::Ok(
|
dioxus::Ok(
|
||||||
reqwest::get("https://dog.ceo/api/breeds/image/random")
|
reqwest::get("https://dog.ceo/api/breeds/image/random")
|
||||||
.await?
|
.await?
|
||||||
.json::<serde_json::Value>()
|
.json::<serde_json::Value>()
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ fn app() -> Element {
|
|||||||
events.push(event);
|
events.push(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
anyhow::Ok(())
|
dioxus::Ok(())
|
||||||
});
|
});
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
|
|||||||
@@ -77,8 +77,11 @@ pub(crate) mod innerlude {
|
|||||||
|
|
||||||
pub use anyhow::anyhow;
|
pub use anyhow::anyhow;
|
||||||
pub use anyhow::Context as AnyhowContext;
|
pub use anyhow::Context as AnyhowContext;
|
||||||
pub use anyhow::Error;
|
// pub use anyhow::Error as AnyhowError;
|
||||||
pub use anyhow::Result;
|
// pub type Error = CapturedError;
|
||||||
|
|
||||||
|
/// A result type with a default error of [`CapturedError`].
|
||||||
|
pub type Result<T, E = CapturedError> = std::result::Result<T, E>;
|
||||||
|
|
||||||
/// An [`Element`] is a possibly-none [`VNode`] created by calling `render` on [`ScopeId`] or [`ScopeState`].
|
/// An [`Element`] is a possibly-none [`VNode`] created by calling `render` on [`ScopeId`] or [`ScopeState`].
|
||||||
///
|
///
|
||||||
@@ -97,7 +100,7 @@ pub use crate::innerlude::{
|
|||||||
suspend, throw_error, try_consume_context, use_after_render, use_before_render, use_drop,
|
suspend, throw_error, try_consume_context, use_after_render, use_before_render, use_drop,
|
||||||
use_hook, use_hook_with_cleanup, with_owner, AnyValue, AnyhowContext, Attribute,
|
use_hook, use_hook_with_cleanup, with_owner, AnyValue, AnyhowContext, Attribute,
|
||||||
AttributeValue, Callback, CapturedError, Component, ComponentFunction, DynamicNode, Element,
|
AttributeValue, Callback, CapturedError, Component, ComponentFunction, DynamicNode, Element,
|
||||||
ElementId, Error, ErrorBoundary, ErrorContext, Event, EventHandler, Fragment, HasAttributes,
|
ElementId, ErrorBoundary, ErrorContext, Event, EventHandler, Fragment, HasAttributes,
|
||||||
IntoAttributeValue, IntoDynNode, LaunchConfig, ListenerCallback, MarkerWrapper, Mutation,
|
IntoAttributeValue, IntoDynNode, LaunchConfig, ListenerCallback, MarkerWrapper, Mutation,
|
||||||
Mutations, NoOpMutations, OptionStringFromMarker, Properties, ReactiveContext, RenderError,
|
Mutations, NoOpMutations, OptionStringFromMarker, Properties, ReactiveContext, RenderError,
|
||||||
Result, Runtime, RuntimeGuard, ScopeId, ScopeState, SpawnIfAsync, SubscriberList, Subscribers,
|
Result, Runtime, RuntimeGuard, ScopeId, ScopeState, SpawnIfAsync, SubscriberList, Subscribers,
|
||||||
@@ -106,5 +109,27 @@ pub use crate::innerlude::{
|
|||||||
VNodeInner, VPlaceholder, VText, VirtualDom, WriteMutations,
|
VNodeInner, VPlaceholder, VText, VirtualDom, WriteMutations,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use anyhow::Ok;
|
/// Equivalent to `Ok::<_, dioxus::CapturedError>(value)`.
|
||||||
|
///
|
||||||
|
/// This simplifies creation of an `dioxus::Result` in places where type
|
||||||
|
/// inference cannot deduce the `E` type of the result — without needing
|
||||||
|
/// to write`Ok::<_, dioxus::CapturedError>(value)`.
|
||||||
|
///
|
||||||
|
/// One might think that `dioxus::Result::Ok(value)` would work in such cases
|
||||||
|
/// but it does not.
|
||||||
|
///
|
||||||
|
/// ```console
|
||||||
|
/// error[E0282]: type annotations needed for `std::result::Result<i32, E>`
|
||||||
|
/// --> src/main.rs:11:13
|
||||||
|
/// |
|
||||||
|
/// 11 | let _ = dioxus::Result::Ok(1);
|
||||||
|
/// | - ^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `E` declared on the enum `Result`
|
||||||
|
/// | |
|
||||||
|
/// | consider giving this pattern the explicit type `std::result::Result<i32, E>`, where the type parameter `E` is specified
|
||||||
|
/// ```
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn Ok<T>(value: T) -> Result<T, CapturedError> {
|
||||||
|
Result::Ok(value)
|
||||||
|
}
|
||||||
|
|
||||||
pub use const_format;
|
pub use const_format;
|
||||||
@@ -51,7 +51,7 @@ impl From<CapturedError> for RenderError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Into<Error>> From<E> for RenderError {
|
impl<E: Into<anyhow::Error>> From<E> for RenderError {
|
||||||
fn from(e: E) -> Self {
|
fn from(e: E) -> Self {
|
||||||
let anyhow_err = e.into();
|
let anyhow_err = e.into();
|
||||||
|
|
||||||
@@ -69,13 +69,44 @@ impl<E: Into<Error>> From<E> for RenderError {
|
|||||||
|
|
||||||
/// An `anyhow::Error` wrapped in an `Arc` so it can be cheaply cloned and passed around.
|
/// An `anyhow::Error` wrapped in an `Arc` so it can be cheaply cloned and passed around.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct CapturedError(pub Arc<Error>);
|
pub struct CapturedError(pub Arc<anyhow::Error>);
|
||||||
|
|
||||||
impl CapturedError {
|
impl CapturedError {
|
||||||
/// Create a `CapturedError` from anything that implements `Display`.
|
/// Create a `CapturedError` from anything that implements `Display`.
|
||||||
pub fn from_display(t: impl Display) -> Self {
|
pub fn from_display(t: impl Display) -> Self {
|
||||||
Self(Arc::new(anyhow::anyhow!(t.to_string())))
|
Self(Arc::new(anyhow::anyhow!(t.to_string())))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a `CapturedError` from anything that implements `std::error::Error`.
|
||||||
|
pub fn new<E>(error: E) -> Self
|
||||||
|
where
|
||||||
|
E: std::error::Error + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
anyhow::Error::new(error).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a `CapturedError` from anything that implements `Display` and `Debug`.
|
||||||
|
pub fn msg<M>(t: M) -> Self
|
||||||
|
where
|
||||||
|
M: Display + Debug + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
anyhow::Error::msg(t).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a `CapturedError` from a boxed `std::error::Error`.
|
||||||
|
pub fn from_boxed(boxed_error: Box<dyn std::error::Error + Send + Sync + 'static>) -> Self {
|
||||||
|
anyhow::Error::from_boxed(boxed_error).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the strong count of the underlying error.
|
||||||
|
pub fn _strong_count(&self) -> usize {
|
||||||
|
std::sync::Arc::strong_count(&self.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try to unwrap the underlying error if this is the only reference to it.
|
||||||
|
pub fn into_inner(self) -> Option<anyhow::Error> {
|
||||||
|
Arc::try_unwrap(self.0).ok()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
@@ -100,14 +131,14 @@ impl<'de> serde::Deserialize<'de> for CapturedError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Deref for CapturedError {
|
impl std::ops::Deref for CapturedError {
|
||||||
type Target = Error;
|
type Target = anyhow::Error;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Into<Error>> From<E> for CapturedError {
|
impl<E: Into<anyhow::Error>> From<E> for CapturedError {
|
||||||
fn from(e: E) -> Self {
|
fn from(e: E) -> Self {
|
||||||
Self(Arc::new(e.into()))
|
Self(Arc::new(e.into()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -331,7 +331,10 @@ impl NativeFileData for DesktopFileData {
|
|||||||
fn read_bytes(
|
fn read_bytes(
|
||||||
&self,
|
&self,
|
||||||
) -> std::pin::Pin<
|
) -> std::pin::Pin<
|
||||||
Box<dyn std::future::Future<Output = Result<bytes::Bytes, dioxus_core::Error>> + 'static>,
|
Box<
|
||||||
|
dyn std::future::Future<Output = Result<bytes::Bytes, dioxus_core::CapturedError>>
|
||||||
|
+ 'static,
|
||||||
|
>,
|
||||||
> {
|
> {
|
||||||
let path = self.0.clone();
|
let path = self.0.clone();
|
||||||
Box::pin(async move { Ok(bytes::Bytes::from(std::fs::read(&path)?)) })
|
Box::pin(async move { Ok(bytes::Bytes::from(std::fs::read(&path)?)) })
|
||||||
@@ -340,7 +343,7 @@ impl NativeFileData for DesktopFileData {
|
|||||||
fn read_string(
|
fn read_string(
|
||||||
&self,
|
&self,
|
||||||
) -> std::pin::Pin<
|
) -> std::pin::Pin<
|
||||||
Box<dyn std::future::Future<Output = Result<String, dioxus_core::Error>> + 'static>,
|
Box<dyn std::future::Future<Output = Result<String, dioxus_core::CapturedError>> + 'static>,
|
||||||
> {
|
> {
|
||||||
let path = self.0.clone();
|
let path = self.0.clone();
|
||||||
Box::pin(async move { Ok(std::fs::read_to_string(&path)?) })
|
Box::pin(async move { Ok(std::fs::read_to_string(&path)?) })
|
||||||
@@ -358,7 +361,7 @@ impl NativeFileData for DesktopFileData {
|
|||||||
&self,
|
&self,
|
||||||
) -> std::pin::Pin<
|
) -> std::pin::Pin<
|
||||||
Box<
|
Box<
|
||||||
dyn futures_util::Stream<Item = Result<bytes::Bytes, dioxus_core::Error>>
|
dyn futures_util::Stream<Item = Result<bytes::Bytes, dioxus_core::CapturedError>>
|
||||||
+ 'static
|
+ 'static
|
||||||
+ Send,
|
+ Send,
|
||||||
>,
|
>,
|
||||||
@@ -374,7 +377,7 @@ impl NativeFileData for DesktopFileData {
|
|||||||
// let n = file
|
// let n = file
|
||||||
// .read(&mut buf)
|
// .read(&mut buf)
|
||||||
// .await
|
// .await
|
||||||
// .map_err(|e| dioxus_core::Error::from(e))?;
|
// .map_err(|e| dioxus_core::CapturedError::from(e))?;
|
||||||
// if n == 0 {
|
// if n == 0 {
|
||||||
// Ok(None)
|
// Ok(None)
|
||||||
// } else {
|
// } else {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
pub use dioxus_core;
|
pub use dioxus_core;
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use dioxus_core::{CapturedError, Error, Ok, Result};
|
pub use dioxus_core::{CapturedError, Ok, Result};
|
||||||
|
|
||||||
#[cfg(feature = "launch")]
|
#[cfg(feature = "launch")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "launch")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "launch")))]
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ pub fn use_loader<F, T, E>(mut future: impl FnMut() -> F + 'static) -> Result<Lo
|
|||||||
where
|
where
|
||||||
F: Future<Output = Result<T, E>> + 'static,
|
F: Future<Output = Result<T, E>> + 'static,
|
||||||
T: 'static + PartialEq + Serialize + DeserializeOwned,
|
T: 'static + PartialEq + Serialize + DeserializeOwned,
|
||||||
E: Into<dioxus_core::Error> + 'static,
|
E: Into<CapturedError> + 'static,
|
||||||
{
|
{
|
||||||
let serialize_context = use_hook(crate::transport::serialize_context);
|
let serialize_context = use_hook(crate::transport::serialize_context);
|
||||||
|
|
||||||
@@ -101,8 +101,8 @@ where
|
|||||||
// Remap the error to the captured error type so it's cheap to clone and pass out, just
|
// Remap the error to the captured error type so it's cheap to clone and pass out, just
|
||||||
// slightly more cumbersome to access the inner error.
|
// slightly more cumbersome to access the inner error.
|
||||||
let out = out.map_err(|e| {
|
let out = out.map_err(|e| {
|
||||||
let anyhow_err: anyhow::Error = e.into();
|
let anyhow_err: CapturedError = e.into();
|
||||||
anyhow_err.into()
|
anyhow_err
|
||||||
});
|
});
|
||||||
|
|
||||||
// If this is the first run and we are on the server, cache the data in the slot we reserved for it
|
// If this is the first run and we are on the server, cache the data in the slot we reserved for it
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
use dioxus_core::CapturedError;
|
use dioxus_core::CapturedError;
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
use std::{cell::RefCell, io::Cursor, rc::Rc, sync::Arc};
|
use std::{cell::RefCell, io::Cursor, rc::Rc};
|
||||||
|
|
||||||
#[cfg(feature = "web")]
|
#[cfg(feature = "web")]
|
||||||
thread_local! {
|
thread_local! {
|
||||||
@@ -429,15 +429,15 @@ pub fn head_element_hydration_entry() -> SerializeContextEntry<bool> {
|
|||||||
|
|
||||||
/// A `Transportable` type can be safely transported from the server to the client, and be used for
|
/// A `Transportable` type can be safely transported from the server to the client, and be used for
|
||||||
/// hydration. Not all types can sensibly be transported, but many can. This trait makes it possible
|
/// hydration. Not all types can sensibly be transported, but many can. This trait makes it possible
|
||||||
/// to customize how types are transported which helps for non-serializable types like `dioxus_core::Error`.
|
/// to customize how types are transported which helps for non-serializable types like `dioxus_core::CapturedError`.
|
||||||
///
|
///
|
||||||
/// By default, all types that implement `Serialize` and `DeserializeOwned` are transportable.
|
/// By default, all types that implement `Serialize` and `DeserializeOwned` are transportable.
|
||||||
///
|
///
|
||||||
/// You can also implement `Transportable` for `Result<T, dioxus_core::Error>` where `T` is
|
/// You can also implement `Transportable` for `Result<T, dioxus_core::CapturedError>` where `T` is
|
||||||
/// `Serialize` and `DeserializeOwned` to allow transporting results that may contain errors.
|
/// `Serialize` and `DeserializeOwned` to allow transporting results that may contain errors.
|
||||||
///
|
///
|
||||||
/// Note that transporting a `Result<T, dioxus_core::Error>` will lose various aspects of the original
|
/// Note that transporting a `Result<T, dioxus_core::CapturedError>` will lose various aspects of the original
|
||||||
/// `dioxus_core::Error` such as backtraces and source errors, but will preserve the error message.
|
/// `dioxus_core::CapturedError` such as backtraces and source errors, but will preserve the error message.
|
||||||
pub trait Transportable<M = ()>: 'static {
|
pub trait Transportable<M = ()>: 'static {
|
||||||
/// Serialize the type to a byte vector for transport
|
/// Serialize the type to a byte vector for transport
|
||||||
fn transport_to_bytes(&self) -> Vec<u8>;
|
fn transport_to_bytes(&self) -> Vec<u8>;
|
||||||
@@ -474,7 +474,7 @@ struct TransportResultErr<T> {
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub struct TransportViaErrMarker;
|
pub struct TransportViaErrMarker;
|
||||||
|
|
||||||
impl<T> Transportable<TransportViaErrMarker> for Result<T, dioxus_core::Error>
|
impl<T> Transportable<TransportViaErrMarker> for Result<T, anyhow::Error>
|
||||||
where
|
where
|
||||||
T: Serialize + DeserializeOwned + 'static,
|
T: Serialize + DeserializeOwned + 'static,
|
||||||
{
|
{
|
||||||
@@ -497,7 +497,7 @@ where
|
|||||||
let err: TransportResultErr<T> = ciborium::from_reader(Cursor::new(bytes))?;
|
let err: TransportResultErr<T> = ciborium::from_reader(Cursor::new(bytes))?;
|
||||||
match err.error {
|
match err.error {
|
||||||
Ok(value) => Ok(Ok(value)),
|
Ok(value) => Ok(Ok(value)),
|
||||||
Err(captured) => Ok(Err(dioxus_core::Error::msg(captured.to_string()))),
|
Err(captured) => Ok(Err(anyhow::Error::msg(captured.to_string()))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -525,8 +525,6 @@ impl Transportable<TransportCapturedError> for CapturedError {
|
|||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
let err: TransportError = ciborium::from_reader(Cursor::new(bytes))?;
|
let err: TransportError = ciborium::from_reader(Cursor::new(bytes))?;
|
||||||
Ok(CapturedError(Arc::new(dioxus_core::Error::msg::<String>(
|
Ok(dioxus_core::CapturedError::msg::<String>(err.error))
|
||||||
err.error,
|
|
||||||
))))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
#![allow(clippy::needless_return)]
|
#![allow(clippy::needless_return)]
|
||||||
|
|
||||||
|
use dioxus_core::CapturedError;
|
||||||
use std::{hint::black_box, prelude::rust_2024::Future, sync::atomic::AtomicBool};
|
use std::{hint::black_box, prelude::rust_2024::Future, sync::atomic::AtomicBool};
|
||||||
|
|
||||||
/// `Lazy` is a thread-safe, lazily-initialized global variable.
|
/// `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> {
|
pub struct Lazy<T> {
|
||||||
value: std::sync::OnceLock<T>,
|
value: std::sync::OnceLock<T>,
|
||||||
started_initialization: AtomicBool,
|
started_initialization: AtomicBool,
|
||||||
constructor: Option<fn() -> Result<T, dioxus_core::Error>>,
|
constructor: Option<fn() -> Result<T, CapturedError>>,
|
||||||
_phantom: std::marker::PhantomData<T>,
|
_phantom: std::marker::PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +37,7 @@ impl<T: Send + Sync + 'static> Lazy<T> {
|
|||||||
where
|
where
|
||||||
F: Fn() -> G + Copy,
|
F: Fn() -> G + Copy,
|
||||||
G: Future<Output = Result<T, E>> + Send + 'static,
|
G: Future<Output = Result<T, E>> + Send + 'static,
|
||||||
E: Into<dioxus_core::Error>,
|
E: Into<CapturedError>,
|
||||||
{
|
{
|
||||||
if std::mem::size_of::<F>() != 0 {
|
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.");
|
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`.
|
/// 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.
|
/// 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);
|
let res = self.value.set(pool);
|
||||||
if res.is_err() {
|
if res.is_err() {
|
||||||
return Err(anyhow::anyhow!("Lazy value is already initialized."));
|
return Err(anyhow::anyhow!("Lazy value is already initialized.").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
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.
|
/// 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 let Some(constructor) = self.constructor {
|
||||||
// If we're already initializing this value, wait on the receiver.
|
// If we're already initializing this value, wait on the receiver.
|
||||||
if self
|
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
|
/// 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.
|
/// 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
|
where
|
||||||
T: Send + Sync + 'static,
|
T: Send + Sync + 'static,
|
||||||
F: Fn() -> G + Copy,
|
F: Fn() -> G + Copy,
|
||||||
G: Future<Output = Result<T, E>> + Send + 'static,
|
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.");
|
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 super::*;
|
||||||
use axum::response::Response;
|
use axum::response::Response;
|
||||||
|
use dioxus_core::CapturedError;
|
||||||
use http::HeaderValue;
|
use http::HeaderValue;
|
||||||
|
|
||||||
/// A trait for converting the result of the Server Function into an Axum response.
|
/// 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>> {
|
impl<T> MakeAxumError<anyhow::Error> for &&ServerFnDecoder<Result<T, anyhow::Error>> {
|
||||||
fn make_axum_error(
|
fn make_axum_error(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ use {
|
|||||||
pub fn use_websocket<
|
pub fn use_websocket<
|
||||||
In: 'static,
|
In: 'static,
|
||||||
Out: 'static,
|
Out: 'static,
|
||||||
E: Into<dioxus_core::Error> + 'static,
|
E: Into<CapturedError> + 'static,
|
||||||
F: Future<Output = Result<Websocket<In, Out, Enc>, E>> + 'static,
|
F: Future<Output = Result<Websocket<In, Out, Enc>, E>> + 'static,
|
||||||
Enc: Encoding,
|
Enc: Encoding,
|
||||||
>(
|
>(
|
||||||
@@ -72,7 +72,7 @@ pub fn use_websocket<
|
|||||||
let status_read = use_hook(|| ReadSignal::new(status));
|
let status_read = use_hook(|| ReadSignal::new(status));
|
||||||
|
|
||||||
let connection = use_resource(move || {
|
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 {
|
async move {
|
||||||
let connection = connection.await;
|
let connection = connection.await;
|
||||||
|
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ impl<T, E> std::future::Future for ServerFnRequest<Result<T, E>> {
|
|||||||
#[diagnostic::on_unimplemented(
|
#[diagnostic::on_unimplemented(
|
||||||
message = "The return type of a server function must be `Result<T, E>`",
|
message = "The return type of a server function must be `Result<T, E>`",
|
||||||
note = "`T` is either `impl IntoResponse` *or* `impl Serialize`",
|
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 {}
|
pub trait AssertIsResult {}
|
||||||
impl<T, E> AssertIsResult for Result<T, E> {}
|
impl<T, E> AssertIsResult for Result<T, E> {}
|
||||||
|
|||||||
@@ -30,17 +30,18 @@ impl FileData {
|
|||||||
self.inner.last_modified()
|
self.inner.last_modified()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_bytes(&self) -> Result<Bytes, dioxus_core::Error> {
|
pub async fn read_bytes(&self) -> Result<Bytes, dioxus_core::CapturedError> {
|
||||||
self.inner.read_bytes().await
|
self.inner.read_bytes().await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_string(&self) -> Result<String, dioxus_core::Error> {
|
pub async fn read_string(&self) -> Result<String, dioxus_core::CapturedError> {
|
||||||
self.inner.read_string().await
|
self.inner.read_string().await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn byte_stream(
|
pub fn byte_stream(
|
||||||
&self,
|
&self,
|
||||||
) -> Pin<Box<dyn Stream<Item = Result<Bytes, dioxus_core::Error>> + Send + 'static>> {
|
) -> Pin<Box<dyn Stream<Item = Result<Bytes, dioxus_core::CapturedError>> + Send + 'static>>
|
||||||
|
{
|
||||||
self.inner.byte_stream()
|
self.inner.byte_stream()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,13 +71,19 @@ pub trait NativeFileData: Send + Sync {
|
|||||||
fn content_type(&self) -> Option<String>;
|
fn content_type(&self) -> Option<String>;
|
||||||
fn read_bytes(
|
fn read_bytes(
|
||||||
&self,
|
&self,
|
||||||
) -> Pin<Box<dyn Future<Output = Result<Bytes, dioxus_core::Error>> + 'static>>;
|
) -> Pin<Box<dyn Future<Output = Result<Bytes, dioxus_core::CapturedError>> + 'static>>;
|
||||||
fn byte_stream(
|
fn byte_stream(
|
||||||
&self,
|
&self,
|
||||||
) -> Pin<Box<dyn futures_util::Stream<Item = Result<Bytes, dioxus_core::Error>> + 'static + Send>>;
|
) -> Pin<
|
||||||
|
Box<
|
||||||
|
dyn futures_util::Stream<Item = Result<Bytes, dioxus_core::CapturedError>>
|
||||||
|
+ 'static
|
||||||
|
+ Send,
|
||||||
|
>,
|
||||||
|
>;
|
||||||
fn read_string(
|
fn read_string(
|
||||||
&self,
|
&self,
|
||||||
) -> Pin<Box<dyn Future<Output = Result<String, dioxus_core::Error>> + 'static>>;
|
) -> Pin<Box<dyn Future<Output = Result<String, dioxus_core::CapturedError>> + 'static>>;
|
||||||
fn inner(&self) -> &dyn std::any::Any;
|
fn inner(&self) -> &dyn std::any::Any;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,7 +150,8 @@ mod serialize {
|
|||||||
|
|
||||||
fn read_bytes(
|
fn read_bytes(
|
||||||
&self,
|
&self,
|
||||||
) -> Pin<Box<dyn Future<Output = Result<Bytes, dioxus_core::Error>> + 'static>> {
|
) -> Pin<Box<dyn Future<Output = Result<Bytes, dioxus_core::CapturedError>> + 'static>>
|
||||||
|
{
|
||||||
let contents = self.contents.clone();
|
let contents = self.contents.clone();
|
||||||
let path = self.path.clone();
|
let path = self.path.clone();
|
||||||
|
|
||||||
@@ -157,13 +165,16 @@ mod serialize {
|
|||||||
return Ok(std::fs::read(path).map(Bytes::from)?);
|
return Ok(std::fs::read(path).map(Bytes::from)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(dioxus_core::Error::msg("File contents not available"))
|
Err(dioxus_core::CapturedError::msg(
|
||||||
|
"File contents not available",
|
||||||
|
))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_string(
|
fn read_string(
|
||||||
&self,
|
&self,
|
||||||
) -> Pin<Box<dyn Future<Output = Result<String, dioxus_core::Error>> + 'static>> {
|
) -> Pin<Box<dyn Future<Output = Result<String, dioxus_core::CapturedError>> + 'static>>
|
||||||
|
{
|
||||||
let contents = self.contents.clone();
|
let contents = self.contents.clone();
|
||||||
let path = self.path.clone();
|
let path = self.path.clone();
|
||||||
|
|
||||||
@@ -177,7 +188,9 @@ mod serialize {
|
|||||||
return Ok(std::fs::read_to_string(path)?);
|
return Ok(std::fs::read_to_string(path)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(dioxus_core::Error::msg("File contents not available"))
|
Err(dioxus_core::CapturedError::msg(
|
||||||
|
"File contents not available",
|
||||||
|
))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,7 +198,9 @@ mod serialize {
|
|||||||
&self,
|
&self,
|
||||||
) -> Pin<
|
) -> Pin<
|
||||||
Box<
|
Box<
|
||||||
dyn futures_util::Stream<Item = Result<Bytes, dioxus_core::Error>> + 'static + Send,
|
dyn futures_util::Stream<Item = Result<Bytes, dioxus_core::CapturedError>>
|
||||||
|
+ 'static
|
||||||
|
+ Send,
|
||||||
>,
|
>,
|
||||||
> {
|
> {
|
||||||
let contents = self.contents.clone();
|
let contents = self.contents.clone();
|
||||||
@@ -201,7 +216,9 @@ mod serialize {
|
|||||||
return Ok(std::fs::read(path).map(Bytes::from)?);
|
return Ok(std::fs::read(path).map(Bytes::from)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(dioxus_core::Error::msg("File contents not available"))
|
Err(dioxus_core::CapturedError::msg(
|
||||||
|
"File contents not available",
|
||||||
|
))
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ impl NativeFileData for WebFileData {
|
|||||||
|
|
||||||
fn read_bytes(
|
fn read_bytes(
|
||||||
&self,
|
&self,
|
||||||
) -> Pin<Box<dyn Future<Output = Result<Bytes, dioxus_core::Error>> + 'static>> {
|
) -> Pin<Box<dyn Future<Output = Result<Bytes, dioxus_core::CapturedError>> + 'static>> {
|
||||||
let file_reader = self.reader.clone();
|
let file_reader = self.reader.clone();
|
||||||
let file_reader_ = self.reader.clone();
|
let file_reader_ = self.reader.clone();
|
||||||
let file = self.file.clone();
|
let file = self.file.clone();
|
||||||
@@ -72,7 +72,7 @@ impl NativeFileData for WebFileData {
|
|||||||
|
|
||||||
fn read_string(
|
fn read_string(
|
||||||
&self,
|
&self,
|
||||||
) -> Pin<Box<dyn Future<Output = Result<String, dioxus_core::Error>> + 'static>> {
|
) -> Pin<Box<dyn Future<Output = Result<String, dioxus_core::CapturedError>> + 'static>> {
|
||||||
let file_reader = self.reader.clone();
|
let file_reader = self.reader.clone();
|
||||||
let file_reader_ = self.reader.clone();
|
let file_reader_ = self.reader.clone();
|
||||||
let file = self.file.clone();
|
let file = self.file.clone();
|
||||||
@@ -113,8 +113,13 @@ impl NativeFileData for WebFileData {
|
|||||||
/// We should maybe update these APIs to use our own custom `ByteBuffer` type to avoid going through `Vec<u8>`?
|
/// We should maybe update these APIs to use our own custom `ByteBuffer` type to avoid going through `Vec<u8>`?
|
||||||
fn byte_stream(
|
fn byte_stream(
|
||||||
&self,
|
&self,
|
||||||
) -> Pin<Box<dyn futures_util::Stream<Item = Result<Bytes, dioxus_core::Error>> + 'static + Send>>
|
) -> Pin<
|
||||||
{
|
Box<
|
||||||
|
dyn futures_util::Stream<Item = Result<Bytes, dioxus_core::CapturedError>>
|
||||||
|
+ 'static
|
||||||
|
+ Send,
|
||||||
|
>,
|
||||||
|
> {
|
||||||
let file = self.file.dyn_ref::<web_sys::Blob>().unwrap().clone();
|
let file = self.file.dyn_ref::<web_sys::Blob>().unwrap().clone();
|
||||||
Box::pin(SendWrapper::new(futures_util::stream::once(async move {
|
Box::pin(SendWrapper::new(futures_util::stream::once(async move {
|
||||||
let array_buff = wasm_bindgen_futures::JsFuture::from(file.array_buffer())
|
let array_buff = wasm_bindgen_futures::JsFuture::from(file.array_buffer())
|
||||||
|
|||||||
Reference in new issue
Block a user