From e2b28a1593fe7acd87b74745c22f35198387f130 Mon Sep 17 00:00:00 2001 From: Ryo Hirayama Date: Fri, 22 Nov 2024 09:47:48 +0900 Subject: [PATCH] Add FromServerFnError trait to server_fn crate --- Cargo.lock | 1 + leptos_server/src/action.rs | 21 +-- leptos_server/src/multi_action.rs | 12 +- server_fn/Cargo.toml | 1 + server_fn/src/client.rs | 35 ++--- server_fn/src/codec/cbor.rs | 62 ++++----- server_fn/src/codec/json.rs | 124 +++++++++--------- server_fn/src/codec/mod.rs | 64 ++++------ server_fn/src/codec/msgpack.rs | 51 ++++---- server_fn/src/codec/multipart.rs | 20 ++- server_fn/src/codec/postcard.rs | 51 ++++---- server_fn/src/codec/rkyv.rs | 56 ++++---- server_fn/src/codec/serde_lite.rs | 62 +++++---- server_fn/src/codec/stream.rs | 119 ++++++++--------- server_fn/src/codec/url.rs | 66 +++++----- server_fn/src/error.rs | 204 ++++++++++++++++++++++++------ server_fn/src/lib.rs | 25 ++-- server_fn/src/middleware/mod.rs | 39 +++--- server_fn/src/request/actix.rs | 43 +++---- server_fn/src/request/axum.rs | 34 ++--- server_fn/src/request/browser.rs | 53 +++++--- server_fn/src/request/generic.rs | 24 ++-- server_fn/src/request/mod.rs | 44 +++---- server_fn/src/request/reqwest.rs | 36 +++--- server_fn/src/request/spin.rs | 24 ++-- server_fn/src/response/actix.rs | 30 ++--- server_fn/src/response/browser.rs | 39 +++--- server_fn/src/response/generic.rs | 39 ++---- server_fn/src/response/http.rs | 40 ++---- server_fn/src/response/mod.rs | 54 +++----- server_fn/src/response/reqwest.rs | 27 ++-- 31 files changed, 772 insertions(+), 728 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 85a9397945..9617ae6e96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3266,6 +3266,7 @@ version = "0.7.0-rc1" dependencies = [ "actix-web", "axum", + "base64", "bytes", "ciborium", "const_format", diff --git a/leptos_server/src/action.rs b/leptos_server/src/action.rs index 177fb9cff1..cbe81b189d 100644 --- a/leptos_server/src/action.rs +++ b/leptos_server/src/action.rs @@ -3,7 +3,10 @@ use reactive_graph::{ owner::use_context, traits::DefinedAt, }; -use server_fn::{error::ServerFnErrorSerde, ServerFn, ServerFnError}; +use server_fn::{ + error::{FromServerFnError, ServerFnErrorSerde}, + ServerFn, +}; use std::{ops::Deref, panic::Location, sync::Arc}; /// An error that can be caused by a server action. @@ -42,7 +45,7 @@ where S: ServerFn + 'static, S::Output: 'static, { - inner: ArcAction>>, + inner: ArcAction>, #[cfg(debug_assertions)] defined_at: &'static Location<'static>, } @@ -52,13 +55,14 @@ where S: ServerFn + Clone + Send + Sync + 'static, S::Output: Send + Sync + 'static, S::Error: Send + Sync + 'static, + S::Error: FromServerFnError, { /// Creates a new [`ArcAction`] that will call the server function `S` when dispatched. #[track_caller] pub fn new() -> Self { let err = use_context::().and_then(|error| { (error.path() == S::PATH) - .then(|| ServerFnError::::de(error.err())) + .then(|| S::Error::de(error.err())) .map(Err) }); Self { @@ -76,7 +80,7 @@ where S: ServerFn + 'static, S::Output: 'static, { - type Target = ArcAction>>; + type Target = ArcAction>; fn deref(&self) -> &Self::Target { &self.inner @@ -131,7 +135,7 @@ where S: ServerFn + 'static, S::Output: 'static, { - inner: Action>>, + inner: Action>, #[cfg(debug_assertions)] defined_at: &'static Location<'static>, } @@ -146,7 +150,7 @@ where pub fn new() -> Self { let err = use_context::().and_then(|error| { (error.path() == S::PATH) - .then(|| ServerFnError::::de(error.err())) + .then(|| S::Error::de(error.err())) .map(Err) }); Self { @@ -182,15 +186,14 @@ where S::Output: Send + Sync + 'static, S::Error: Send + Sync + 'static, { - type Target = Action>>; + type Target = Action>; fn deref(&self) -> &Self::Target { &self.inner } } -impl From> - for Action>> +impl From> for Action> where S: ServerFn + 'static, S::Output: 'static, diff --git a/leptos_server/src/multi_action.rs b/leptos_server/src/multi_action.rs index d62a6a9054..64e7fc2985 100644 --- a/leptos_server/src/multi_action.rs +++ b/leptos_server/src/multi_action.rs @@ -2,7 +2,7 @@ use reactive_graph::{ actions::{ArcMultiAction, MultiAction}, traits::DefinedAt, }; -use server_fn::{ServerFn, ServerFnError}; +use server_fn::ServerFn; use std::{ops::Deref, panic::Location}; /// An [`ArcMultiAction`] that can be used to call a server function. @@ -11,7 +11,7 @@ where S: ServerFn + 'static, S::Output: 'static, { - inner: ArcMultiAction>>, + inner: ArcMultiAction>, #[cfg(debug_assertions)] defined_at: &'static Location<'static>, } @@ -40,7 +40,7 @@ where S: ServerFn + 'static, S::Output: 'static, { - type Target = ArcMultiAction>>; + type Target = ArcMultiAction>; fn deref(&self) -> &Self::Target { &self.inner @@ -95,13 +95,13 @@ where S: ServerFn + 'static, S::Output: 'static, { - inner: MultiAction>>, + inner: MultiAction>, #[cfg(debug_assertions)] defined_at: &'static Location<'static>, } impl From> - for MultiAction>> + for MultiAction> where S: ServerFn + 'static, S::Output: 'static, @@ -152,7 +152,7 @@ where S::Output: 'static, S::Error: 'static, { - type Target = MultiAction>>; + type Target = MultiAction>; fn deref(&self) -> &Self::Target { &self.inner diff --git a/server_fn/Cargo.toml b/server_fn/Cargo.toml index 98e56e3b57..088fe80942 100644 --- a/server_fn/Cargo.toml +++ b/server_fn/Cargo.toml @@ -53,6 +53,7 @@ bytes = "1.8" http-body-util = { version = "0.1.2", optional = true } rkyv = { version = "0.8.8", optional = true } rmp-serde = { version = "1.3.0", optional = true } +base64 = { version = "0.22.1" } # client gloo-net = { version = "0.6.0", optional = true } diff --git a/server_fn/src/client.rs b/server_fn/src/client.rs index 502fd4c4b7..47026d29a8 100644 --- a/server_fn/src/client.rs +++ b/server_fn/src/client.rs @@ -1,4 +1,4 @@ -use crate::{error::ServerFnError, request::ClientReq, response::ClientRes}; +use crate::{request::ClientReq, response::ClientRes}; use std::{future::Future, sync::OnceLock}; static ROOT_URL: OnceLock<&'static str> = OnceLock::new(); @@ -21,16 +21,16 @@ pub fn get_server_url() -> &'static str { /// This trait is implemented for things like a browser `fetch` request or for /// the `reqwest` trait. It should almost never be necessary to implement it /// yourself, unless you’re trying to use an alternative HTTP crate on the client side. -pub trait Client { +pub trait Client { /// The type of a request sent by this client. - type Request: ClientReq + Send; + type Request: ClientReq + Send; /// The type of a response received by this client. - type Response: ClientRes + Send; + type Response: ClientRes + Send; /// Sends the request and receives a response. fn send( req: Self::Request, - ) -> impl Future>> + Send; + ) -> impl Future> + Send; } #[cfg(feature = "browser")] @@ -38,24 +38,23 @@ pub trait Client { pub mod browser { use super::Client; use crate::{ - error::ServerFnError, + error::{FromServerFnError, ServerFnErrorErr}, request::browser::{BrowserRequest, RequestInner}, response::browser::BrowserResponse, }; use send_wrapper::SendWrapper; use std::future::Future; - /// Implements [`Client`] for a `fetch` request in the browser. + /// Implements [`Client`] for a `fetch` request in the browser. pub struct BrowserClient; - impl Client for BrowserClient { + impl Client for BrowserClient { type Request = BrowserRequest; type Response = BrowserResponse; fn send( req: Self::Request, - ) -> impl Future>> - + Send { + ) -> impl Future> + Send { SendWrapper::new(async move { let req = req.0.take(); let RequestInner { @@ -66,7 +65,9 @@ pub mod browser { .send() .await .map(|res| BrowserResponse(SendWrapper::new(res))) - .map_err(|e| ServerFnError::Request(e.to_string())); + .map_err(|e| { + ServerFnErrorErr::Request(e.to_string()).into() + }); // at this point, the future has successfully resolved without being dropped, so we // can prevent the `AbortController` from firing @@ -83,7 +84,10 @@ pub mod browser { /// Implements [`Client`] for a request made by [`reqwest`]. pub mod reqwest { use super::Client; - use crate::{error::ServerFnError, request::reqwest::CLIENT}; + use crate::{ + error::{FromServerFnError, ServerFnErrorErr}, + request::reqwest::CLIENT, + }; use futures::TryFutureExt; use reqwest::{Request, Response}; use std::future::Future; @@ -91,17 +95,16 @@ pub mod reqwest { /// Implements [`Client`] for a request made by [`reqwest`]. pub struct ReqwestClient; - impl Client for ReqwestClient { + impl Client for ReqwestClient { type Request = Request; type Response = Response; fn send( req: Self::Request, - ) -> impl Future>> - + Send { + ) -> impl Future> + Send { CLIENT .execute(req) - .map_err(|e| ServerFnError::Request(e.to_string())) + .map_err(|e| ServerFnErrorErr::Request(e.to_string()).into()) } } } diff --git a/server_fn/src/codec/cbor.rs b/server_fn/src/codec/cbor.rs index a5a91c8117..25d3d2a193 100644 --- a/server_fn/src/codec/cbor.rs +++ b/server_fn/src/codec/cbor.rs @@ -1,6 +1,6 @@ use super::{Encoding, FromReq, FromRes, IntoReq, IntoRes}; use crate::{ - error::ServerFnError, + error::{FromServerFnError, ServerFnErrorErr}, request::{ClientReq, Req}, response::{ClientRes, Res}, }; @@ -16,19 +16,17 @@ impl Encoding for Cbor { const METHOD: Method = Method::POST; } -impl IntoReq for T +impl IntoReq for T where - Request: ClientReq, + Request: ClientReq, T: Serialize + Send, + E: FromServerFnError, { - fn into_req( - self, - path: &str, - accepts: &str, - ) -> Result> { + fn into_req(self, path: &str, accepts: &str) -> Result { let mut buffer: Vec = Vec::new(); - ciborium::ser::into_writer(&self, &mut buffer) - .map_err(|e| ServerFnError::Serialization(e.to_string()))?; + ciborium::ser::into_writer(&self, &mut buffer).map_err(|e| { + E::from(ServerFnErrorErr::Serialization(e.to_string())) + })?; Request::try_new_post_bytes( path, accepts, @@ -38,40 +36,44 @@ where } } -impl FromReq for T +impl FromReq for T where - Request: Req + Send + 'static, + Request: Req + Send + 'static, T: DeserializeOwned, + E: FromServerFnError, { - async fn from_req(req: Request) -> Result> { + async fn from_req(req: Request) -> Result { let body_bytes = req.try_into_bytes().await?; ciborium::de::from_reader(body_bytes.as_ref()) - .map_err(|e| ServerFnError::Args(e.to_string())) + .map_err(|e| E::from(ServerFnErrorErr::Args(e.to_string()))) } } -impl IntoRes for T +impl IntoRes for T where - Response: Res, + Response: Res, T: Serialize + Send, + E: FromServerFnError, { - async fn into_res(self) -> Result> { + async fn into_res(self) -> Result { let mut buffer: Vec = Vec::new(); - ciborium::ser::into_writer(&self, &mut buffer) - .map_err(|e| ServerFnError::Serialization(e.to_string()))?; + ciborium::ser::into_writer(&self, &mut buffer).map_err(|e| { + E::from(ServerFnErrorErr::Serialization(e.to_string())) + })?; Response::try_from_bytes(Cbor::CONTENT_TYPE, Bytes::from(buffer)) } } -impl FromRes for T +impl FromRes for T where - Response: ClientRes + Send, + Response: ClientRes + Send, T: DeserializeOwned + Send, + E: FromServerFnError, { - async fn from_res(res: Response) -> Result> { + async fn from_res(res: Response) -> Result { let data = res.try_into_bytes().await?; ciborium::de::from_reader(data.as_ref()) - .map_err(|e| ServerFnError::Args(e.to_string())) + .map_err(|e| E::from(ServerFnErrorErr::Args(e.to_string()))) } } @@ -114,20 +116,20 @@ where ::Data: Send , ::Data: Send , { - async fn from_req(req: http::Request) -> Result> { + async fn from_req(req: http::Request) -> Result> { let (_parts, body) = req.into_parts(); let body_bytes = body .collect() .await .map(|c| c.to_bytes()) - .map_err(|e| ServerFnError::Deserialization(e.to_string()))?; + .map_err(|e| ServerFnErrorErr::Deserialization(e.to_string()).into())?; let data = ciborium::de::from_reader(body_bytes.as_ref()) - .map_err(|e| ServerFnError::Args(e.to_string()))?; + .map_err(|e| ServerFnErrorErr::Args(e.to_string()).into())?; Ok(data) } - async fn into_req(self) -> Result, ServerFnError> { + async fn into_req(self) -> Result, ServerFnError> { let mut buffer: Vec = Vec::new(); ciborium::ser::into_writer(&self, &mut buffer)?; let req = http::Request::builder() @@ -139,17 +141,17 @@ where .body(Body::from(buffer))?; Ok(req) } - async fn from_res(res: http::Response) -> Result> { + async fn from_res(res: http::Response) -> Result> { let (_parts, body) = res.into_parts(); let body_bytes = body .collect() .await .map(|c| c.to_bytes()) - .map_err(|e| ServerFnError::Deserialization(e.to_string()))?; + .map_err(|e| ServerFnErrorErr::Deserialization(e.to_string()).into())?; ciborium::de::from_reader(body_bytes.as_ref()) - .map_err(|e| ServerFnError::Args(e.to_string())) + .map_err(|e| ServerFnErrorErr::Args(e.to_string()).into()) } async fn into_res(self) -> http::Response { diff --git a/server_fn/src/codec/json.rs b/server_fn/src/codec/json.rs index d4324d61d6..2df9c8c185 100644 --- a/server_fn/src/codec/json.rs +++ b/server_fn/src/codec/json.rs @@ -1,6 +1,6 @@ use super::{Encoding, FromReq, FromRes, Streaming}; use crate::{ - error::{NoCustomError, ServerFnError}, + error::{FromServerFnError, ServerFnErrorErr}, request::{ClientReq, Req}, response::{ClientRes, Res}, IntoReq, IntoRes, @@ -18,55 +18,58 @@ impl Encoding for Json { const METHOD: Method = Method::POST; } -impl IntoReq for T +impl IntoReq for T where - Request: ClientReq, + Request: ClientReq, T: Serialize + Send, + E: FromServerFnError, { - fn into_req( - self, - path: &str, - accepts: &str, - ) -> Result> { - let data = serde_json::to_string(&self) - .map_err(|e| ServerFnError::Serialization(e.to_string()))?; + fn into_req(self, path: &str, accepts: &str) -> Result { + let data = serde_json::to_string(&self).map_err(|e| { + E::from(ServerFnErrorErr::Serialization(e.to_string())) + })?; Request::try_new_post(path, accepts, Json::CONTENT_TYPE, data) } } -impl FromReq for T +impl FromReq for T where - Request: Req + Send + 'static, + Request: Req + Send + 'static, T: DeserializeOwned, + E: FromServerFnError, { - async fn from_req(req: Request) -> Result> { + async fn from_req(req: Request) -> Result { let string_data = req.try_into_string().await?; serde_json::from_str::(&string_data) - .map_err(|e| ServerFnError::Args(e.to_string())) + .map_err(|e| E::from(ServerFnErrorErr::Args(e.to_string()))) } } -impl IntoRes for T +impl IntoRes for T where - Response: Res, + Response: Res, T: Serialize + Send, + E: FromServerFnError, { - async fn into_res(self) -> Result> { - let data = serde_json::to_string(&self) - .map_err(|e| ServerFnError::Serialization(e.to_string()))?; + async fn into_res(self) -> Result { + let data = serde_json::to_string(&self).map_err(|e| { + E::from(ServerFnErrorErr::Serialization(e.to_string())) + })?; Response::try_from_string(Json::CONTENT_TYPE, data) } } -impl FromRes for T +impl FromRes for T where - Response: ClientRes + Send, + Response: ClientRes + Send, T: DeserializeOwned + Send, + E: FromServerFnError, { - async fn from_res(res: Response) -> Result> { + async fn from_res(res: Response) -> Result { let data = res.try_into_string().await?; - serde_json::from_str(&data) - .map_err(|e| ServerFnError::Deserialization(e.to_string())) + serde_json::from_str(&data).map_err(|e| { + E::from(ServerFnErrorErr::Deserialization(e.to_string())) + }) } } @@ -102,35 +105,31 @@ impl Encoding for StreamingJson { /// end before the output will begin. /// /// Streaming requests are only allowed over HTTP2 or HTTP3. -pub struct JsonStream( - Pin>> + Send>>, -); +pub struct JsonStream(Pin> + Send>>); -impl std::fmt::Debug for JsonStream { +impl std::fmt::Debug for JsonStream { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("JsonStream").finish() } } -impl JsonStream { +impl JsonStream { /// Creates a new `ByteStream` from the given stream. pub fn new( - value: impl Stream> + Send + 'static, + value: impl Stream> + Send + 'static, ) -> Self { Self(Box::pin(value.map(|value| value.map(Into::into)))) } } -impl JsonStream { +impl JsonStream { /// Consumes the wrapper, returning a stream of text. - pub fn into_inner( - self, - ) -> impl Stream>> + Send { + pub fn into_inner(self) -> impl Stream> + Send { self.0 } } -impl From for JsonStream +impl From for JsonStream where S: Stream + Send + 'static, { @@ -139,18 +138,15 @@ where } } -impl IntoReq for S +impl IntoReq for S where - Request: ClientReq, + Request: ClientReq, S: Stream + Send + 'static, T: Serialize + 'static, + E: FromServerFnError + Serialize, { - fn into_req( - self, - path: &str, - accepts: &str, - ) -> Result> { - let data: JsonStream = self.into(); + fn into_req(self, path: &str, accepts: &str) -> Result { + let data: JsonStream = self.into(); Request::try_new_streaming( path, accepts, @@ -164,56 +160,58 @@ where } } -impl FromReq for S +impl FromReq for S where - Request: Req + Send + 'static, + Request: Req + Send + 'static, // The additional `Stream` bound is never used, but it is required to avoid an error where `T` is unconstrained - S: Stream + From> + Send + 'static, + S: Stream + From> + Send + 'static, T: DeserializeOwned + 'static, + E: FromServerFnError, { - async fn from_req(req: Request) -> Result> { + async fn from_req(req: Request) -> Result { let data = req.try_into_stream()?; let s = JsonStream::new(data.map(|chunk| { chunk.and_then(|bytes| { - serde_json::from_slice(bytes.as_ref()) - .map_err(|e| ServerFnError::Deserialization(e.to_string())) + serde_json::from_slice(bytes.as_ref()).map_err(|e| { + E::from(ServerFnErrorErr::Deserialization(e.to_string())) + }) }) })); Ok(s.into()) } } -impl IntoRes - for JsonStream +impl IntoRes for JsonStream where - Response: Res, - CustErr: 'static, + Response: Res, T: Serialize + 'static, + E: FromServerFnError, { - async fn into_res(self) -> Result> { + async fn into_res(self) -> Result { Response::try_from_stream( Streaming::CONTENT_TYPE, self.into_inner().map(|value| { - serde_json::to_vec(&value?) - .map(Bytes::from) - .map_err(|e| ServerFnError::Serialization(e.to_string())) + serde_json::to_vec(&value?).map(Bytes::from).map_err(|e| { + ServerFnErrorErr::Serialization(e.to_string()).into() + }) }), ) } } -impl FromRes - for JsonStream +impl FromRes for JsonStream where - Response: ClientRes + Send, + Response: ClientRes + Send, T: DeserializeOwned, + E: FromServerFnError, { - async fn from_res(res: Response) -> Result> { + async fn from_res(res: Response) -> Result { let stream = res.try_into_stream()?; Ok(JsonStream::new(stream.map(|chunk| { chunk.and_then(|bytes| { - serde_json::from_slice(bytes.as_ref()) - .map_err(|e| ServerFnError::Deserialization(e.to_string())) + serde_json::from_slice(bytes.as_ref()).map_err(|e| { + ServerFnErrorErr::Deserialization(e.to_string()).into() + }) }) }))) } diff --git a/server_fn/src/codec/mod.rs b/server_fn/src/codec/mod.rs index fc5bb37846..33676cfb7f 100644 --- a/server_fn/src/codec/mod.rs +++ b/server_fn/src/codec/mod.rs @@ -55,7 +55,6 @@ mod postcard; pub use postcard::*; mod stream; -use crate::error::ServerFnError; use futures::Future; use http::Method; pub use stream::*; @@ -71,31 +70,27 @@ pub use stream::*; /// For example, here’s the implementation for [`Json`]. /// /// ```rust,ignore -/// impl IntoReq for T +/// impl IntoReq for T /// where -/// Request: ClientReq, +/// Request: ClientReq, /// T: Serialize + Send, /// { /// fn into_req( /// self, /// path: &str, /// accepts: &str, -/// ) -> Result> { +/// ) -> Result { /// // try to serialize the data /// let data = serde_json::to_string(&self) -/// .map_err(|e| ServerFnError::Serialization(e.to_string()))?; +/// .map_err(|e| ServerFnErrorErr::Serialization(e.to_string()).into())?; /// // and use it as the body of a POST request /// Request::try_new_post(path, accepts, Json::CONTENT_TYPE, data) /// } /// } /// ``` -pub trait IntoReq { +pub trait IntoReq { /// Attempts to serialize the arguments into an HTTP request. - fn into_req( - self, - path: &str, - accepts: &str, - ) -> Result>; + fn into_req(self, path: &str, accepts: &str) -> Result; } /// Deserializes an HTTP request into the data type, on the server. @@ -109,32 +104,31 @@ pub trait IntoReq { /// For example, here’s the implementation for [`Json`]. /// /// ```rust,ignore -/// impl FromReq for T +/// impl FromReq for T /// where /// // require the Request implement `Req` -/// Request: Req + Send + 'static, +/// Request: Req + Send + 'static, /// // require that the type can be deserialized with `serde` /// T: DeserializeOwned, +/// E: FromServerFnError, /// { /// async fn from_req( /// req: Request, -/// ) -> Result> { +/// ) -> Result { /// // try to convert the body of the request into a `String` /// let string_data = req.try_into_string().await?; /// // deserialize the data -/// serde_json::from_str::(&string_data) -/// .map_err(|e| ServerFnError::Args(e.to_string())) +/// serde_json::from_str(&string_data) +/// .map_err(|e| ServerFnErrorErr::Args(e.to_string()).into()) /// } /// } /// ``` -pub trait FromReq +pub trait FromReq where Self: Sized, { /// Attempts to deserialize the arguments from a request. - fn from_req( - req: Request, - ) -> impl Future>> + Send; + fn from_req(req: Request) -> impl Future> + Send; } /// Serializes the data type into an HTTP response. @@ -148,25 +142,24 @@ where /// For example, here’s the implementation for [`Json`]. /// /// ```rust,ignore -/// impl IntoRes for T +/// impl IntoRes for T /// where -/// Response: Res, +/// Response: Res, /// T: Serialize + Send, +/// E: FromServerFnError, /// { -/// async fn into_res(self) -> Result> { +/// async fn into_res(self) -> Result { /// // try to serialize the data /// let data = serde_json::to_string(&self) -/// .map_err(|e| ServerFnError::Serialization(e.to_string()))?; +/// .map_err(|e| ServerFnErrorErr::Serialization(e.to_string()).into())?; /// // and use it as the body of a response /// Response::try_from_string(Json::CONTENT_TYPE, data) /// } /// } /// ``` -pub trait IntoRes { +pub trait IntoRes { /// Attempts to serialize the output into an HTTP response. - fn into_res( - self, - ) -> impl Future>> + Send; + fn into_res(self) -> impl Future> + Send; } /// Deserializes the data type from an HTTP response. @@ -181,30 +174,29 @@ pub trait IntoRes { /// For example, here’s the implementation for [`Json`]. /// /// ```rust,ignore -/// impl FromRes for T +/// impl FromRes for T /// where -/// Response: ClientRes + Send, +/// Response: ClientRes + Send, /// T: DeserializeOwned + Send, +/// E: FromServerFnError, /// { /// async fn from_res( /// res: Response, -/// ) -> Result> { +/// ) -> Result { /// // extracts the request body /// let data = res.try_into_string().await?; /// // and tries to deserialize it as JSON /// serde_json::from_str(&data) -/// .map_err(|e| ServerFnError::Deserialization(e.to_string())) +/// .map_err(|e| ServerFnErrorErr::Deserialization(e.to_string()).into()) /// } /// } /// ``` -pub trait FromRes +pub trait FromRes where Self: Sized, { /// Attempts to deserialize the outputs from a response. - fn from_res( - res: Response, - ) -> impl Future>> + Send; + fn from_res(res: Response) -> impl Future> + Send; } /// Defines a particular encoding format, which can be used for serializing or deserializing data. diff --git a/server_fn/src/codec/msgpack.rs b/server_fn/src/codec/msgpack.rs index c06789e1a6..5ba9c0d253 100644 --- a/server_fn/src/codec/msgpack.rs +++ b/server_fn/src/codec/msgpack.rs @@ -1,6 +1,6 @@ use super::{Encoding, FromReq, FromRes, IntoReq, IntoRes}; use crate::{ - error::ServerFnError, + error::{FromServerFnError, ServerFnErrorErr}, request::{ClientReq, Req}, response::{ClientRes, Res}, }; @@ -16,18 +16,16 @@ impl Encoding for MsgPack { const METHOD: Method = Method::POST; } -impl IntoReq for T +impl IntoReq for T where - Request: ClientReq, + Request: ClientReq, T: Serialize, + E: FromServerFnError, { - fn into_req( - self, - path: &str, - accepts: &str, - ) -> Result> { - let data = rmp_serde::to_vec(&self) - .map_err(|e| ServerFnError::Serialization(e.to_string()))?; + fn into_req(self, path: &str, accepts: &str) -> Result { + let data = rmp_serde::to_vec(&self).map_err(|e| { + E::from(ServerFnErrorErr::Serialization(e.to_string())) + })?; Request::try_new_post_bytes( path, MsgPack::CONTENT_TYPE, @@ -37,38 +35,43 @@ where } } -impl FromReq for T +impl FromReq for T where - Request: Req + Send, + Request: Req + Send, T: DeserializeOwned, + E: FromServerFnError, { - async fn from_req(req: Request) -> Result> { + async fn from_req(req: Request) -> Result { let data = req.try_into_bytes().await?; rmp_serde::from_slice::(&data) - .map_err(|e| ServerFnError::Args(e.to_string())) + .map_err(|e| ServerFnErrorErr::Args(e.to_string()).into()) } } -impl IntoRes for T +impl IntoRes for T where - Response: Res, + Response: Res, T: Serialize + Send, + E: FromServerFnError, { - async fn into_res(self) -> Result> { - let data = rmp_serde::to_vec(&self) - .map_err(|e| ServerFnError::Serialization(e.to_string()))?; + async fn into_res(self) -> Result { + let data = rmp_serde::to_vec(&self).map_err(|e| { + E::from(ServerFnErrorErr::Serialization(e.to_string())) + })?; Response::try_from_bytes(MsgPack::CONTENT_TYPE, Bytes::from(data)) } } -impl FromRes for T +impl FromRes for T where - Response: ClientRes + Send, + Response: ClientRes + Send, T: DeserializeOwned, + E: FromServerFnError, { - async fn from_res(res: Response) -> Result> { + async fn from_res(res: Response) -> Result { let data = res.try_into_bytes().await?; - rmp_serde::from_slice(&data) - .map_err(|e| ServerFnError::Deserialization(e.to_string())) + rmp_serde::from_slice(&data).map_err(|e| { + E::from(ServerFnErrorErr::Deserialization(e.to_string())) + }) } } diff --git a/server_fn/src/codec/multipart.rs b/server_fn/src/codec/multipart.rs index 998e822260..f1ddb98117 100644 --- a/server_fn/src/codec/multipart.rs +++ b/server_fn/src/codec/multipart.rs @@ -1,6 +1,6 @@ use super::{Encoding, FromReq}; use crate::{ - error::ServerFnError, + error::FromServerFnError, request::{browser::BrowserFormData, ClientReq, Req}, IntoReq, }; @@ -56,16 +56,12 @@ impl From for MultipartData { } } -impl IntoReq for T +impl IntoReq for T where - Request: ClientReq, + Request: ClientReq, T: Into, { - fn into_req( - self, - path: &str, - accepts: &str, - ) -> Result> { + fn into_req(self, path: &str, accepts: &str) -> Result { let multi = self.into(); Request::try_new_multipart( path, @@ -75,13 +71,13 @@ where } } -impl FromReq for T +impl FromReq for T where - Request: Req + Send + 'static, + Request: Req + Send + 'static, T: From, - CustErr: 'static, + E: FromServerFnError, { - async fn from_req(req: Request) -> Result> { + async fn from_req(req: Request) -> Result { let boundary = req .to_content_type() .and_then(|ct| multer::parse_boundary(ct).ok()) diff --git a/server_fn/src/codec/postcard.rs b/server_fn/src/codec/postcard.rs index b78dae63b2..65f80cfcb0 100644 --- a/server_fn/src/codec/postcard.rs +++ b/server_fn/src/codec/postcard.rs @@ -1,6 +1,6 @@ use super::{Encoding, FromReq, FromRes, IntoReq, IntoRes}; use crate::{ - error::ServerFnError, + error::{FromServerFnError, ServerFnErrorErr}, request::{ClientReq, Req}, response::{ClientRes, Res}, }; @@ -16,18 +16,16 @@ impl Encoding for Postcard { const METHOD: Method = Method::POST; } -impl IntoReq for T +impl IntoReq for T where - Request: ClientReq, + Request: ClientReq, T: Serialize, + E: FromServerFnError, { - fn into_req( - self, - path: &str, - accepts: &str, - ) -> Result> { - let data = postcard::to_allocvec(&self) - .map_err(|e| ServerFnError::Serialization(e.to_string()))?; + fn into_req(self, path: &str, accepts: &str) -> Result { + let data = postcard::to_allocvec(&self).map_err(|e| { + E::from(ServerFnErrorErr::Serialization(e.to_string())) + })?; Request::try_new_post_bytes( path, Postcard::CONTENT_TYPE, @@ -37,38 +35,43 @@ where } } -impl FromReq for T +impl FromReq for T where - Request: Req + Send, + Request: Req + Send, T: DeserializeOwned, + E: FromServerFnError, { - async fn from_req(req: Request) -> Result> { + async fn from_req(req: Request) -> Result { let data = req.try_into_bytes().await?; postcard::from_bytes::(&data) - .map_err(|e| ServerFnError::Args(e.to_string())) + .map_err(|e| E::from(ServerFnErrorErr::Args(e.to_string()))) } } -impl IntoRes for T +impl IntoRes for T where - Response: Res, + Response: Res, T: Serialize + Send, + E: FromServerFnError, { - async fn into_res(self) -> Result> { - let data = postcard::to_allocvec(&self) - .map_err(|e| ServerFnError::Serialization(e.to_string()))?; + async fn into_res(self) -> Result { + let data = postcard::to_allocvec(&self).map_err(|e| { + E::from(ServerFnErrorErr::Serialization(e.to_string())) + })?; Response::try_from_bytes(Postcard::CONTENT_TYPE, Bytes::from(data)) } } -impl FromRes for T +impl FromRes for T where - Response: ClientRes + Send, + Response: ClientRes + Send, T: DeserializeOwned, + E: FromServerFnError, { - async fn from_res(res: Response) -> Result> { + async fn from_res(res: Response) -> Result { let data = res.try_into_bytes().await?; - postcard::from_bytes(&data) - .map_err(|e| ServerFnError::Deserialization(e.to_string())) + postcard::from_bytes(&data).map_err(|e| { + E::from(ServerFnErrorErr::Deserialization(e.to_string())) + }) } } diff --git a/server_fn/src/codec/rkyv.rs b/server_fn/src/codec/rkyv.rs index 9aed0ff83a..86643b3709 100644 --- a/server_fn/src/codec/rkyv.rs +++ b/server_fn/src/codec/rkyv.rs @@ -1,6 +1,6 @@ use super::{Encoding, FromReq, FromRes, IntoReq, IntoRes}; use crate::{ - error::ServerFnError, + error::{FromServerFnError, ServerFnErrorErr}, request::{ClientReq, Req}, response::{ClientRes, Res}, }; @@ -29,39 +29,41 @@ impl Encoding for Rkyv { const METHOD: Method = Method::POST; } -impl IntoReq for T +impl IntoReq for T where - Request: ClientReq, + Request: ClientReq, T: Archive + for<'a> Serialize>, T::Archived: Deserialize + for<'a> CheckBytes>, + E: FromServerFnError, { - fn into_req( - self, - path: &str, - accepts: &str, - ) -> Result> { - let encoded = rkyv::to_bytes::(&self) - .map_err(|e| ServerFnError::Serialization(e.to_string()))?; + fn into_req(self, path: &str, accepts: &str) -> Result { + let encoded = rkyv::to_bytes::(&self).map_err(|e| { + E::from(ServerFnErrorErr::Serialization(e.to_string())) + })?; let bytes = Bytes::copy_from_slice(encoded.as_ref()); Request::try_new_post_bytes(path, accepts, Rkyv::CONTENT_TYPE, bytes) } } -impl FromReq for T +impl FromReq for T where - Request: Req + Send + 'static, + Request: Req + Send + 'static, T: Archive + for<'a> Serialize>, T::Archived: Deserialize + for<'a> CheckBytes>, + E: FromServerFnError, { - async fn from_req(req: Request) -> Result> { + async fn from_req(req: Request) -> Result { let mut aligned = AlignedVec::<1024>::new(); let mut body_stream = Box::pin(req.try_into_stream()?); while let Some(chunk) = body_stream.next().await { match chunk { Err(e) => { - return Err(ServerFnError::Deserialization(e.to_string())) + return Err(ServerFnErrorErr::Deserialization( + e.to_string(), + ) + .into()) } Ok(bytes) => { for byte in bytes { @@ -71,36 +73,40 @@ where } } rkyv::from_bytes::(aligned.as_ref()) - .map_err(|e| ServerFnError::Args(e.to_string())) + .map_err(|e| ServerFnErrorErr::Args(e.to_string()).into()) } } -impl IntoRes for T +impl IntoRes for T where - Response: Res, + Response: Res, T: Send, T: Archive + for<'a> Serialize>, T::Archived: Deserialize + for<'a> CheckBytes>, + E: FromServerFnError, { - async fn into_res(self) -> Result> { - let encoded = rkyv::to_bytes::(&self) - .map_err(|e| ServerFnError::Serialization(format!("{e:?}")))?; + async fn into_res(self) -> Result { + let encoded = rkyv::to_bytes::(&self).map_err(|e| { + E::from(ServerFnErrorErr::Serialization(format!("{e:?}"))) + })?; let bytes = Bytes::copy_from_slice(encoded.as_ref()); Response::try_from_bytes(Rkyv::CONTENT_TYPE, bytes) } } -impl FromRes for T +impl FromRes for T where - Response: ClientRes + Send, + Response: ClientRes + Send, T: Archive + for<'a> Serialize>, T::Archived: Deserialize + for<'a> CheckBytes>, + E: FromServerFnError, { - async fn from_res(res: Response) -> Result> { + async fn from_res(res: Response) -> Result { let data = res.try_into_bytes().await?; - rkyv::from_bytes::(&data) - .map_err(|e| ServerFnError::Deserialization(e.to_string())) + rkyv::from_bytes::(&data).map_err(|e| { + ServerFnErrorErr::Deserialization(e.to_string()).into() + }) } } diff --git a/server_fn/src/codec/serde_lite.rs b/server_fn/src/codec/serde_lite.rs index b71b9390fe..319d698a81 100644 --- a/server_fn/src/codec/serde_lite.rs +++ b/server_fn/src/codec/serde_lite.rs @@ -1,6 +1,6 @@ use super::{Encoding, FromReq, FromRes}; use crate::{ - error::ServerFnError, + error::{FromServerFnError, ServerFnErrorErr}, request::{ClientReq, Req}, response::{ClientRes, Res}, IntoReq, IntoRes, @@ -15,68 +15,64 @@ impl Encoding for SerdeLite { const METHOD: Method = Method::POST; } -impl IntoReq for T +impl IntoReq for T where - Request: ClientReq, + Request: ClientReq, T: Serialize + Send, + E: FromServerFnError, { - fn into_req( - self, - path: &str, - accepts: &str, - ) -> Result> { - let data = serde_json::to_string( - &self - .serialize() - .map_err(|e| ServerFnError::Serialization(e.to_string()))?, - ) - .map_err(|e| ServerFnError::Serialization(e.to_string()))?; + fn into_req(self, path: &str, accepts: &str) -> Result { + let data = serde_json::to_string(&self.serialize().map_err(|e| { + E::from(ServerFnErrorErr::Serialization(e.to_string())) + })?) + .map_err(|e| E::from(ServerFnErrorErr::Serialization(e.to_string())))?; Request::try_new_post(path, accepts, SerdeLite::CONTENT_TYPE, data) } } -impl FromReq for T +impl FromReq for T where - Request: Req + Send + 'static, + Request: Req + Send + 'static, T: Deserialize, + E: FromServerFnError, { - async fn from_req(req: Request) -> Result> { + async fn from_req(req: Request) -> Result { let string_data = req.try_into_string().await?; Self::deserialize( &serde_json::from_str(&string_data) - .map_err(|e| ServerFnError::Args(e.to_string()))?, + .map_err(|e| E::from(ServerFnErrorErr::Args(e.to_string())))?, ) - .map_err(|e| ServerFnError::Args(e.to_string())) + .map_err(|e| E::from(ServerFnErrorErr::Args(e.to_string()))) } } -impl IntoRes for T +impl IntoRes for T where - Response: Res, + Response: Res, T: Serialize + Send, + E: FromServerFnError, { - async fn into_res(self) -> Result> { - let data = serde_json::to_string( - &self - .serialize() - .map_err(|e| ServerFnError::Serialization(e.to_string()))?, - ) - .map_err(|e| ServerFnError::Serialization(e.to_string()))?; + async fn into_res(self) -> Result { + let data = serde_json::to_string(&self.serialize().map_err(|e| { + E::from(ServerFnErrorErr::Serialization(e.to_string())) + })?) + .map_err(|e| E::from(ServerFnErrorErr::Serialization(e.to_string())))?; Response::try_from_string(SerdeLite::CONTENT_TYPE, data) } } -impl FromRes for T +impl FromRes for T where - Response: ClientRes + Send, + Response: ClientRes + Send, T: Deserialize + Send, + E: FromServerFnError, { - async fn from_res(res: Response) -> Result> { + async fn from_res(res: Response) -> Result { let data = res.try_into_string().await?; Self::deserialize( &serde_json::from_str(&data) - .map_err(|e| ServerFnError::Args(e.to_string()))?, + .map_err(|e| E::from(ServerFnErrorErr::Args(e.to_string())))?, ) - .map_err(|e| ServerFnError::Deserialization(e.to_string())) + .map_err(|e| E::from(ServerFnErrorErr::Deserialization(e.to_string()))) } } diff --git a/server_fn/src/codec/stream.rs b/server_fn/src/codec/stream.rs index 15e2765363..95958f968a 100644 --- a/server_fn/src/codec/stream.rs +++ b/server_fn/src/codec/stream.rs @@ -1,6 +1,6 @@ use super::{Encoding, FromReq, FromRes, IntoReq}; use crate::{ - error::{NoCustomError, ServerFnError}, + error::{FromServerFnError, ServerFnErrorErr}, request::{ClientReq, Req}, response::{ClientRes, Res}, IntoRes, @@ -29,26 +29,22 @@ impl Encoding for Streaming { const METHOD: Method = Method::POST; } -impl IntoReq for T +impl IntoReq for T where - Request: ClientReq, + Request: ClientReq, T: Stream + Send + Sync + 'static, { - fn into_req( - self, - path: &str, - accepts: &str, - ) -> Result> { + fn into_req(self, path: &str, accepts: &str) -> Result { Request::try_new_streaming(path, accepts, Streaming::CONTENT_TYPE, self) } } -impl FromReq for T +impl FromReq for T where - Request: Req + Send + 'static, - T: From + 'static, + Request: Req + Send + 'static, + T: From> + 'static, { - async fn from_req(req: Request) -> Result> { + async fn from_req(req: Request) -> Result { let data = req.try_into_stream()?; let s = ByteStream::new(data); Ok(s.into()) @@ -67,29 +63,25 @@ where /// end before the output will begin. /// /// Streaming requests are only allowed over HTTP2 or HTTP3. -pub struct ByteStream( - Pin>> + Send>>, -); +pub struct ByteStream(Pin> + Send>>); -impl ByteStream { +impl ByteStream { /// Consumes the wrapper, returning a stream of bytes. - pub fn into_inner( - self, - ) -> impl Stream>> + Send { + pub fn into_inner(self) -> impl Stream> + Send { self.0 } } -impl Debug for ByteStream { +impl Debug for ByteStream { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("ByteStream").finish() } } -impl ByteStream { +impl ByteStream { /// Creates a new `ByteStream` from the given stream. pub fn new( - value: impl Stream> + Send + 'static, + value: impl Stream> + Send + 'static, ) -> Self where T: Into, @@ -98,7 +90,7 @@ impl ByteStream { } } -impl From for ByteStream +impl From for ByteStream where S: Stream + Send + 'static, T: Into, @@ -108,22 +100,21 @@ where } } -impl IntoRes - for ByteStream +impl IntoRes for ByteStream where - Response: Res, - CustErr: 'static, + Response: Res, + E: 'static, { - async fn into_res(self) -> Result> { + async fn into_res(self) -> Result { Response::try_from_stream(Streaming::CONTENT_TYPE, self.into_inner()) } } -impl FromRes for ByteStream +impl FromRes for ByteStream where - Response: ClientRes + Send, + Response: ClientRes + Send, { - async fn from_res(res: Response) -> Result> { + async fn from_res(res: Response) -> Result { let stream = res.try_into_stream()?; Ok(ByteStream(Box::pin(stream))) } @@ -160,35 +151,31 @@ impl Encoding for StreamingText { /// end before the output will begin. /// /// Streaming requests are only allowed over HTTP2 or HTTP3. -pub struct TextStream( - Pin>> + Send>>, -); +pub struct TextStream(Pin> + Send>>); -impl Debug for TextStream { +impl Debug for TextStream { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("TextStream").finish() } } -impl TextStream { +impl TextStream { /// Creates a new `ByteStream` from the given stream. pub fn new( - value: impl Stream> + Send + 'static, + value: impl Stream> + Send + 'static, ) -> Self { Self(Box::pin(value.map(|value| value.map(Into::into)))) } } -impl TextStream { +impl TextStream { /// Consumes the wrapper, returning a stream of text. - pub fn into_inner( - self, - ) -> impl Stream>> + Send { + pub fn into_inner(self) -> impl Stream> + Send { self.0 } } -impl From for TextStream +impl From for TextStream where S: Stream + Send + 'static, T: Into, @@ -198,16 +185,13 @@ where } } -impl IntoReq for T +impl IntoReq for T where - Request: ClientReq, - T: Into, + Request: ClientReq, + T: Into>, + E: 'static, { - fn into_req( - self, - path: &str, - accepts: &str, - ) -> Result> { + fn into_req(self, path: &str, accepts: &str) -> Result { let data = self.into(); Request::try_new_streaming( path, @@ -218,30 +202,31 @@ where } } -impl FromReq for T +impl FromReq for T where - Request: Req + Send + 'static, - T: From + 'static, + Request: Req + Send + 'static, + T: From> + 'static, + E: FromServerFnError, { - async fn from_req(req: Request) -> Result> { + async fn from_req(req: Request) -> Result { let data = req.try_into_stream()?; let s = TextStream::new(data.map(|chunk| { chunk.and_then(|bytes| { - String::from_utf8(bytes.to_vec()) - .map_err(|e| ServerFnError::Deserialization(e.to_string())) + String::from_utf8(bytes.to_vec()).map_err(|e| { + ServerFnErrorErr::Deserialization(e.to_string()).into() + }) }) })); Ok(s.into()) } } -impl IntoRes - for TextStream +impl IntoRes for TextStream where - Response: Res, - CustErr: 'static, + Response: Res, + E: 'static, { - async fn into_res(self) -> Result> { + async fn into_res(self) -> Result { Response::try_from_stream( Streaming::CONTENT_TYPE, self.into_inner().map(|stream| stream.map(Into::into)), @@ -249,16 +234,18 @@ where } } -impl FromRes for TextStream +impl FromRes for TextStream where - Response: ClientRes + Send, + Response: ClientRes + Send, + E: FromServerFnError, { - async fn from_res(res: Response) -> Result> { + async fn from_res(res: Response) -> Result { let stream = res.try_into_stream()?; Ok(TextStream(Box::pin(stream.map(|chunk| { chunk.and_then(|bytes| { - String::from_utf8(bytes.into()) - .map_err(|e| ServerFnError::Deserialization(e.to_string())) + String::from_utf8(bytes.into()).map_err(|e| { + ServerFnErrorErr::Deserialization(e.to_string()).into() + }) }) })))) } diff --git a/server_fn/src/codec/url.rs b/server_fn/src/codec/url.rs index 38e7894649..79eca2d7d5 100644 --- a/server_fn/src/codec/url.rs +++ b/server_fn/src/codec/url.rs @@ -1,6 +1,6 @@ use super::{Encoding, FromReq, IntoReq}; use crate::{ - error::ServerFnError, + error::{FromServerFnError, ServerFnErrorErr}, request::{ClientReq, Req}, }; use http::Method; @@ -17,32 +17,31 @@ impl Encoding for GetUrl { const METHOD: Method = Method::GET; } -impl IntoReq for T +impl IntoReq for T where - Request: ClientReq, + Request: ClientReq, T: Serialize + Send, + E: FromServerFnError, { - fn into_req( - self, - path: &str, - accepts: &str, - ) -> Result> { - let data = serde_qs::to_string(&self) - .map_err(|e| ServerFnError::Serialization(e.to_string()))?; + fn into_req(self, path: &str, accepts: &str) -> Result { + let data = serde_qs::to_string(&self).map_err(|e| { + E::from(ServerFnErrorErr::Serialization(e.to_string())) + })?; Request::try_new_get(path, accepts, GetUrl::CONTENT_TYPE, &data) } } -impl FromReq for T +impl FromReq for T where - Request: Req + Send + 'static, + Request: Req + Send + 'static, T: DeserializeOwned, + E: FromServerFnError, { - async fn from_req(req: Request) -> Result> { + async fn from_req(req: Request) -> Result { let string_data = req.as_query().unwrap_or_default(); let args = serde_qs::Config::new(5, false) .deserialize_str::(string_data) - .map_err(|e| ServerFnError::Args(e.to_string()))?; + .map_err(|e| E::from(ServerFnErrorErr::Args(e.to_string())))?; Ok(args) } } @@ -52,32 +51,31 @@ impl Encoding for PostUrl { const METHOD: Method = Method::POST; } -impl IntoReq for T +impl IntoReq for T where - Request: ClientReq, + Request: ClientReq, T: Serialize + Send, + E: FromServerFnError, { - fn into_req( - self, - path: &str, - accepts: &str, - ) -> Result> { - let qs = serde_qs::to_string(&self) - .map_err(|e| ServerFnError::Serialization(e.to_string()))?; + fn into_req(self, path: &str, accepts: &str) -> Result { + let qs = serde_qs::to_string(&self).map_err(|e| { + E::from(ServerFnErrorErr::Serialization(e.to_string())) + })?; Request::try_new_post(path, accepts, PostUrl::CONTENT_TYPE, qs) } } -impl FromReq for T +impl FromReq for T where - Request: Req + Send + 'static, + Request: Req + Send + 'static, T: DeserializeOwned, + E: FromServerFnError, { - async fn from_req(req: Request) -> Result> { + async fn from_req(req: Request) -> Result { let string_data = req.try_into_string().await?; let args = serde_qs::Config::new(5, false) .deserialize_str::(&string_data) - .map_err(|e| ServerFnError::Args(e.to_string()))?; + .map_err(|e| E::from(ServerFnErrorErr::Args(e.to_string())))?; Ok(args) } } @@ -86,18 +84,18 @@ where impl Codec for T where T: DeserializeOwned + Serialize + Send, - Request: Req + Send, - Response: Res + Send, + Request: Req + Send, + Response: Res + Send, { - async fn from_req(req: Request) -> Result> { + async fn from_req(req: Request) -> Result> { let string_data = req.try_into_string()?; let args = serde_json::from_str::(&string_data) - .map_err(|e| ServerFnError::Args(e.to_string()))?; + .map_err(|e| ServerFnErrorErr::Args(e.to_string()).into())?; Ok(args) } - async fn into_req(self) -> Result> { + async fn into_req(self) -> Result> { /* let qs = serde_qs::to_string(&self)?; let req = http::Request::builder() .method("GET") @@ -110,7 +108,7 @@ where todo!() } - async fn from_res(res: Response) -> Result> { + async fn from_res(res: Response) -> Result> { todo!() /* let (_parts, body) = res.into_parts(); @@ -118,7 +116,7 @@ where .collect() .await .map(|c| c.to_bytes()) - .map_err(|e| ServerFnError::Deserialization(e.to_string()))?; + .map_err(|e| ServerFnErrorErr::Deserialization(e.to_string()).into())?; let string_data = String::from_utf8(body_bytes.to_vec())?; serde_json::from_str(&string_data) .map_err(|e| ServerFnError::Deserialization(e.to_string())) */ diff --git a/server_fn/src/error.rs b/server_fn/src/error.rs index c139dde062..aa1781fb98 100644 --- a/server_fn/src/error.rs +++ b/server_fn/src/error.rs @@ -1,4 +1,7 @@ -use serde::{Deserialize, Serialize}; +#![allow(deprecated)] + +use base64::{engine::general_purpose::URL_SAFE, Engine as _}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; use std::{ fmt, fmt::{Display, Write}, @@ -35,6 +38,11 @@ impl From for Error { feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize) )] +#[deprecated( + since = "0.8.0", + note = "Now server_fn can return any error type other than ServerFnError, \ + so the WrappedServerError variant will be removed in 0.9.0" +)] pub struct NoCustomError; // Implement `Display` for `NoCustomError` @@ -55,11 +63,21 @@ impl FromStr for NoCustomError { /// Wraps some error type, which may implement any of [`Error`](trait@std::error::Error), [`Clone`], or /// [`Display`]. #[derive(Debug)] +#[deprecated( + since = "0.8.0", + note = "Now server_fn can return any error type other than ServerFnError, \ + so the WrappedServerError variant will be removed in 0.9.0" +)] pub struct WrapError(pub T); /// A helper macro to convert a variety of different types into `ServerFnError`. /// This should mostly be used if you are implementing `From` for `YourError`. #[macro_export] +#[deprecated( + since = "0.8.0", + note = "Now server_fn can return any error type other than ServerFnError, \ + so the WrappedServerError variant will be removed in 0.9.0" +)] macro_rules! server_fn_error { () => {{ use $crate::{ViaError, WrapError}; @@ -75,6 +93,12 @@ macro_rules! server_fn_error { /// This trait serves as the conversion method between a variety of types /// and [`ServerFnError`]. +#[deprecated( + since = "0.8.0", + note = "Now server_fn can return any error type other than ServerFnError, \ + so users should place their custom error type instead of \ + ServerFnError" +)] pub trait ViaError { /// Converts something into an error. fn to_server_error(&self) -> ServerFnError; @@ -90,6 +114,7 @@ impl ViaError } // A type tag for ServerFnError so we can special case it +#[deprecated] pub(crate) trait ServerFnErrorKind {} impl ServerFnErrorKind for ServerFnError {} @@ -132,6 +157,7 @@ impl ViaError for WrapError { } /// Type for errors that can occur when using server functions. +/// This type is intended to be used as the return type of the server function for easy error conversion with `?` operator. /// /// Unlike [`ServerFnErrorErr`], this does not implement [`Error`](trait@std::error::Error). /// This means that other error types can easily be converted into it using the @@ -142,6 +168,12 @@ impl ViaError for WrapError { derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize) )] pub enum ServerFnError { + #[deprecated( + since = "0.8.0", + note = "Now server_fn can return any error type other than \ + ServerFnError, so users should place their custom error type \ + instead of ServerFnError" + )] /// A user-defined custom error type, which defaults to [`NoCustomError`]. WrappedServerError(E), /// Error while trying to register the server function (only occurs in case of poisoned RwLock). @@ -152,6 +184,8 @@ pub enum ServerFnError { Response(String), /// Occurs when there is an error while actually running the function on the server. ServerError(String), + /// Occurs when there is an error while actually running the middleware on the server. + MiddlewareError(String), /// Occurs on the client if there is an error deserializing the server's response. Deserialization(String), /// Occurs on the client if there is an error serializing the server function arguments. @@ -198,6 +232,8 @@ where ), ServerFnError::ServerError(s) => format!("error running server function: {s}"), + ServerFnError::MiddlewareError(s) => + format!("error running middleware: {s}"), ServerFnError::Deserialization(s) => format!("error deserializing server function results: {s}"), ServerFnError::Serialization(s) => @@ -224,39 +260,47 @@ where /// /// This is implemented for the default [`ServerFnError`], which uses [`NoCustomError`]. pub trait ServerFnErrorSerde: Sized { + /// The error type that can occur when serializing the custom error type. + type Error: std::error::Error; + /// Converts the custom error type to a [`String`]. - fn ser(&self) -> Result; + fn ser(&self) -> Result; /// Deserializes the custom error type from a [`String`]. fn de(data: &str) -> Self; } -impl ServerFnErrorSerde for ServerFnError +impl ServerFnErrorSerde for ServerFnErrorErr where CustErr: FromStr + Display, { - fn ser(&self) -> Result { + type Error = std::fmt::Error; + + fn ser(&self) -> Result { let mut buf = String::new(); match self { - ServerFnError::WrappedServerError(e) => { + ServerFnErrorErr::WrappedServerError(e) => { write!(&mut buf, "WrappedServerFn|{e}") } - ServerFnError::Registration(e) => { + ServerFnErrorErr::Registration(e) => { write!(&mut buf, "Registration|{e}") } - ServerFnError::Request(e) => write!(&mut buf, "Request|{e}"), - ServerFnError::Response(e) => write!(&mut buf, "Response|{e}"), - ServerFnError::ServerError(e) => { + ServerFnErrorErr::Request(e) => write!(&mut buf, "Request|{e}"), + ServerFnErrorErr::Response(e) => write!(&mut buf, "Response|{e}"), + ServerFnErrorErr::ServerError(e) => { write!(&mut buf, "ServerError|{e}") } - ServerFnError::Deserialization(e) => { + ServerFnErrorErr::MiddlewareError(e) => { + write!(&mut buf, "MiddlewareError|{e}") + } + ServerFnErrorErr::Deserialization(e) => { write!(&mut buf, "Deserialization|{e}") } - ServerFnError::Serialization(e) => { + ServerFnErrorErr::Serialization(e) => { write!(&mut buf, "Serialization|{e}") } - ServerFnError::Args(e) => write!(&mut buf, "Args|{e}"), - ServerFnError::MissingArg(e) => { + ServerFnErrorErr::Args(e) => write!(&mut buf, "Args|{e}"), + ServerFnErrorErr::MissingArg(e) => { write!(&mut buf, "MissingArg|{e}") } }?; @@ -267,31 +311,33 @@ where data.split_once('|') .and_then(|(ty, data)| match ty { "WrappedServerFn" => match CustErr::from_str(data) { - Ok(d) => Some(ServerFnError::WrappedServerError(d)), + Ok(d) => Some(ServerFnErrorErr::WrappedServerError(d)), Err(_) => None, }, "Registration" => { - Some(ServerFnError::Registration(data.to_string())) + Some(ServerFnErrorErr::Registration(data.to_string())) + } + "Request" => Some(ServerFnErrorErr::Request(data.to_string())), + "Response" => { + Some(ServerFnErrorErr::Response(data.to_string())) } - "Request" => Some(ServerFnError::Request(data.to_string())), - "Response" => Some(ServerFnError::Response(data.to_string())), "ServerError" => { - Some(ServerFnError::ServerError(data.to_string())) + Some(ServerFnErrorErr::ServerError(data.to_string())) } "Deserialization" => { - Some(ServerFnError::Deserialization(data.to_string())) + Some(ServerFnErrorErr::Deserialization(data.to_string())) } "Serialization" => { - Some(ServerFnError::Serialization(data.to_string())) + Some(ServerFnErrorErr::Serialization(data.to_string())) } - "Args" => Some(ServerFnError::Args(data.to_string())), + "Args" => Some(ServerFnErrorErr::Args(data.to_string())), "MissingArg" => { - Some(ServerFnError::MissingArg(data.to_string())) + Some(ServerFnErrorErr::MissingArg(data.to_string())) } _ => None, }) .unwrap_or_else(|| { - ServerFnError::Deserialization(format!( + ServerFnErrorErr::Deserialization(format!( "Could not deserialize error {data:?}" )) }) @@ -311,7 +357,7 @@ where } } -/// Type for errors that can occur when using server functions. +/// Type for errors that can occur when using server functions. If you need to return a custom error type from a server function, implement `From` for your custom error type. /// /// Unlike [`ServerFnError`], this implements [`std::error::Error`]. This means /// it can be used in situations in which the `Error` trait is required, but it’s @@ -334,6 +380,9 @@ pub enum ServerFnErrorErr { /// Occurs when there is an error while actually running the function on the server. #[error("error running server function: {0}")] ServerError(String), + /// Occurs when there is an error while actually running the middleware on the server. + #[error("error running middleware: {0}")] + MiddlewareError(String), /// Occurs on the client if there is an error deserializing the server's response. #[error("error deserializing server function results: {0}")] Deserialization(String), @@ -361,6 +410,9 @@ impl From> for ServerFnErrorErr { ServerFnError::ServerError(value) => { ServerFnErrorErr::ServerError(value) } + ServerFnError::MiddlewareError(value) => { + ServerFnErrorErr::MiddlewareError(value) + } ServerFnError::Deserialization(value) => { ServerFnErrorErr::Deserialization(value) } @@ -386,15 +438,15 @@ impl From> for ServerFnErrorErr { /// without JavaScript/WASM supported, by encoding it in the URL as a query string. /// This is useful for progressive enhancement. #[derive(Debug)] -pub struct ServerFnUrlError { +pub struct ServerFnUrlError { path: String, - error: ServerFnError, + error: E, } -impl ServerFnUrlError { +impl ServerFnUrlError { /// Creates a new structure associating the server function at some path /// with a particular error. - pub fn new(path: impl Display, error: ServerFnError) -> Self { + pub fn new(path: impl Display, error: E) -> Self { Self { path: path.to_string(), error, @@ -402,7 +454,7 @@ impl ServerFnUrlError { } /// The error itself. - pub fn error(&self) -> &ServerFnError { + pub fn error(&self) -> &E { &self.error } @@ -412,16 +464,13 @@ impl ServerFnUrlError { } /// Adds an encoded form of this server function error to the given base URL. - pub fn to_url(&self, base: &str) -> Result - where - CustErr: FromStr + Display, - { + pub fn to_url(&self, base: &str) -> Result { let mut url = Url::parse(base)?; url.query_pairs_mut() .append_pair("__path", &self.path) .append_pair( "__err", - &ServerFnErrorSerde::ser(&self.error).unwrap_or_default(), + &URL_SAFE.encode(self.error.ser().unwrap_or_default()), ); Ok(url) } @@ -448,16 +497,93 @@ impl ServerFnUrlError { *path = url.to_string(); } } + + /// Decodes an error from a URL. + pub fn decode_err(err: &str) -> E { + let decoded = match URL_SAFE.decode(err) { + Ok(decoded) => decoded, + Err(err) => { + return ServerFnErrorErr::Deserialization(err.to_string()) + .into() + } + }; + let s = match String::from_utf8(decoded) { + Ok(s) => s, + Err(err) => { + return ServerFnErrorErr::Deserialization(err.to_string()) + .into() + } + }; + E::de(&s) + } } -impl From> for ServerFnError { - fn from(error: ServerFnUrlError) -> Self { +impl From> for ServerFnError { + fn from(error: ServerFnUrlError) -> Self { + error.error.into() + } +} + +impl From>> for ServerFnError { + fn from(error: ServerFnUrlError>) -> Self { error.error } } -impl From> for ServerFnErrorErr { - fn from(error: ServerFnUrlError) -> Self { - error.error.into() +#[derive(Debug)] +#[doc(hidden)] +/// Only used instantly only when a framework needs E: Error. +pub struct ServerFnErrorWrapper(pub E); + +impl Display for ServerFnErrorWrapper { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl std::error::Error for ServerFnErrorWrapper { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + None + } +} + +/// A trait for types that can be returned from a server function. +pub trait FromServerFnError: + Display + + std::fmt::Debug + + ServerFnErrorSerde + + From + + 'static +{ +} + +#[test] +fn assert_from_server_fn_error_impl() { + fn assert_impl() {} + + assert_impl::(); +} + +impl FromServerFnError for E where + E: Display + + std::fmt::Debug + + ServerFnErrorSerde + + From + + 'static +{ +} + +impl ServerFnErrorSerde for E +where + E: Serialize + DeserializeOwned, +{ + type Error = serde_json::Error; + + fn ser(&self) -> Result { + serde_json::to_string(self) + } + + fn de(data: &str) -> Self { + serde_json::from_str(data).unwrap() } } diff --git a/server_fn/src/lib.rs b/server_fn/src/lib.rs index 5b656d33a1..bd2ba5afa5 100644 --- a/server_fn/src/lib.rs +++ b/server_fn/src/lib.rs @@ -131,10 +131,9 @@ use codec::{Encoding, FromReq, FromRes, IntoReq, IntoRes}; #[doc(hidden)] pub use const_format; use dashmap::DashMap; -pub use error::ServerFnError; -use error::ServerFnErrorSerde; #[cfg(feature = "form-redirects")] use error::ServerFnUrlError; +use error::{FromServerFnError, ServerFnErrorSerde}; use http::Method; use middleware::{Layer, Service}; use once_cell::sync::Lazy; @@ -148,7 +147,7 @@ pub use serde; #[doc(hidden)] #[cfg(feature = "serde-lite")] pub use serde_lite; -use std::{fmt::Display, future::Future, pin::Pin, str::FromStr, sync::Arc}; +use std::{future::Future, pin::Pin, sync::Arc}; #[doc(hidden)] pub use xxhash_rust; @@ -222,9 +221,8 @@ where /// The [`Encoding`] used in the response for the result of the server function. type OutputEncoding: Encoding; - /// The type of the custom error on [`ServerFnError`], if any. (If there is no - /// custom error type, this can be `NoCustomError` by default.) - type Error: FromStr + Display; + /// The type of the error on the server function. Typically [`ServerFnError`], but allowed to be any type that implements [`FromServerFnError`]. + type Error: FromServerFnError; /// Returns [`Self::PATH`]. fn url() -> &'static str { @@ -240,7 +238,7 @@ where /// The body of the server function. This will only run on the server. fn run_body( self, - ) -> impl Future>> + Send; + ) -> impl Future> + Send; #[doc(hidden)] fn run_on_server( @@ -298,8 +296,7 @@ where #[doc(hidden)] fn run_on_client( self, - ) -> impl Future>> + Send - { + ) -> impl Future> + Send { async move { // create and send request on client let req = @@ -313,8 +310,7 @@ where fn run_on_client_with_req( req: >::Request, redirect_hook: Option<&RedirectHook>, - ) -> impl Future>> + Send - { + ) -> impl Future> + Send { async move { let res = Self::Client::send(req).await?; @@ -325,7 +321,7 @@ where // if it returns an error status, deserialize the error using FromStr let res = if (400..=599).contains(&status) { let text = res.try_into_string().await?; - Err(ServerFnError::::de(&text)) + Err(Self::Error::de(&text)) } else { // otherwise, deserialize the body as is Ok(Self::Output::from_res(res).await) @@ -345,9 +341,8 @@ where #[doc(hidden)] fn execute_on_server( req: Self::ServerRequest, - ) -> impl Future< - Output = Result>, - > + Send { + ) -> impl Future> + Send + { async { let this = Self::from_req(req).await?; let output = this.run_body().await?; diff --git a/server_fn/src/middleware/mod.rs b/server_fn/src/middleware/mod.rs index 0789bf719a..dba6d4c204 100644 --- a/server_fn/src/middleware/mod.rs +++ b/server_fn/src/middleware/mod.rs @@ -29,20 +29,19 @@ pub trait Service { #[cfg(feature = "axum-no-default")] mod axum { use super::{BoxedService, Service}; - use crate::{response::Res, ServerFnError}; + use crate::{ + error::{FromServerFnError, ServerFnErrorErr}, + response::Res, + }; use axum::body::Body; use http::{Request, Response}; - use std::{ - fmt::{Debug, Display}, - future::Future, - pin::Pin, - }; + use std::{future::Future, pin::Pin}; impl super::Service, Response> for S where S: tower::Service, Response = Response>, S::Future: Send + 'static, - S::Error: Into + Send + Debug + Display + Sync + 'static, + S::Error: FromServerFnError + Send + Sync + 'static, { fn run( &mut self, @@ -52,7 +51,9 @@ mod axum { let inner = self.call(req); Box::pin(async move { inner.await.unwrap_or_else(|e| { - let err = ServerFnError::new(e); + let err = S::Error::from( + ServerFnErrorErr::MiddlewareError(e.to_string()), + ); Response::::error_response(&path, &err) }) }) @@ -63,7 +64,7 @@ mod axum { for BoxedService, Response> { type Response = Response; - type Error = ServerFnError; + type Error = ServerFnErrorErr; type Future = Pin< Box< dyn std::future::Future< @@ -105,22 +106,18 @@ mod axum { #[cfg(feature = "actix")] mod actix { use crate::{ + error::{FromServerFnError, ServerFnErrorErr}, request::actix::ActixRequest, response::{actix::ActixResponse, Res}, - ServerFnError, }; use actix_web::{HttpRequest, HttpResponse}; - use std::{ - fmt::{Debug, Display}, - future::Future, - pin::Pin, - }; + use std::{future::Future, pin::Pin}; impl super::Service for S where S: actix_web::dev::Service, S::Future: Send + 'static, - S::Error: Into + Debug + Display + 'static, + S::Error: FromServerFnError, { fn run( &mut self, @@ -130,7 +127,9 @@ mod actix { let inner = self.call(req); Box::pin(async move { inner.await.unwrap_or_else(|e| { - let err = ServerFnError::new(e); + let err = S::Error::from( + ServerFnErrorErr::MiddlewareError(e.to_string()), + ); ActixResponse::error_response(&path, &err).take() }) }) @@ -141,7 +140,7 @@ mod actix { where S: actix_web::dev::Service, S::Future: Send + 'static, - S::Error: Into + Debug + Display + 'static, + S::Error: FromServerFnError, { fn run( &mut self, @@ -151,7 +150,9 @@ mod actix { let inner = self.call(req.0.take().0); Box::pin(async move { ActixResponse::from(inner.await.unwrap_or_else(|e| { - let err = ServerFnError::new(e); + let err = S::Error::from( + ServerFnErrorErr::MiddlewareError(e.to_string()), + ); ActixResponse::error_response(&path, &err).take() })) }) diff --git a/server_fn/src/request/actix.rs b/server_fn/src/request/actix.rs index 90e8e1f26a..98365f9575 100644 --- a/server_fn/src/request/actix.rs +++ b/server_fn/src/request/actix.rs @@ -1,4 +1,7 @@ -use crate::{error::ServerFnError, request::Req}; +use crate::{ + error::{FromServerFnError, ServerFnErrorErr}, + request::Req, +}; use actix_web::{web::Payload, HttpRequest}; use bytes::Bytes; use futures::Stream; @@ -33,9 +36,9 @@ impl From<(HttpRequest, Payload)> for ActixRequest { } } -impl Req for ActixRequest +impl Req for ActixRequest where - CustErr: 'static, + E: FromServerFnError, { fn as_query(&self) -> Option<&str> { self.0 .0.uri().query() @@ -53,44 +56,34 @@ where self.header("Referer") } - fn try_into_bytes( - self, - ) -> impl Future>> + Send - { + fn try_into_bytes(self) -> impl Future> + Send { // Actix is going to keep this on a single thread anyway so it's fine to wrap it // with SendWrapper, which makes it `Send` but will panic if it moves to another thread SendWrapper::new(async move { let payload = self.0.take().1; - payload - .to_bytes() - .await - .map_err(|e| ServerFnError::Deserialization(e.to_string())) + payload.to_bytes().await.map_err(|e| { + ServerFnErrorErr::Deserialization(e.to_string()).into() + }) }) } - fn try_into_string( - self, - ) -> impl Future>> + Send - { + fn try_into_string(self) -> impl Future> + Send { // Actix is going to keep this on a single thread anyway so it's fine to wrap it // with SendWrapper, which makes it `Send` but will panic if it moves to another thread SendWrapper::new(async move { let payload = self.0.take().1; - let bytes = payload - .to_bytes() - .await - .map_err(|e| ServerFnError::Deserialization(e.to_string()))?; - String::from_utf8(bytes.into()) - .map_err(|e| ServerFnError::Deserialization(e.to_string())) + let bytes = payload.to_bytes().await.map_err(|e| { + E::from(ServerFnErrorErr::Deserialization(e.to_string())) + })?; + String::from_utf8(bytes.into()).map_err(|e| { + E::from(ServerFnErrorErr::Deserialization(e.to_string())) + }) }) } fn try_into_stream( self, - ) -> Result< - impl Stream> + Send, - ServerFnError, - > { + ) -> Result> + Send, E> { Ok(futures::stream::once(async { todo!() })) } } diff --git a/server_fn/src/request/axum.rs b/server_fn/src/request/axum.rs index e26f7c7676..1ae63c3f1b 100644 --- a/server_fn/src/request/axum.rs +++ b/server_fn/src/request/axum.rs @@ -1,4 +1,7 @@ -use crate::{error::ServerFnError, request::Req}; +use crate::{ + error::{FromServerFnError, ServerFnErrorErr}, + request::Req, +}; use axum::body::{Body, Bytes}; use futures::{Stream, StreamExt}; use http::{ @@ -8,9 +11,9 @@ use http::{ use http_body_util::BodyExt; use std::borrow::Cow; -impl Req for Request +impl Req for Request where - CustErr: 'static, + E: FromServerFnError, { fn as_query(&self) -> Option<&str> { self.uri().query() @@ -34,29 +37,28 @@ where .map(|h| String::from_utf8_lossy(h.as_bytes())) } - async fn try_into_bytes(self) -> Result> { + async fn try_into_bytes(self) -> Result { let (_parts, body) = self.into_parts(); - body.collect() - .await - .map(|c| c.to_bytes()) - .map_err(|e| ServerFnError::Deserialization(e.to_string())) + body.collect().await.map(|c| c.to_bytes()).map_err(|e| { + ServerFnErrorErr::Deserialization(e.to_string()).into() + }) } - async fn try_into_string(self) -> Result> { + async fn try_into_string(self) -> Result { let bytes = self.try_into_bytes().await?; - String::from_utf8(bytes.to_vec()) - .map_err(|e| ServerFnError::Deserialization(e.to_string())) + String::from_utf8(bytes.to_vec()).map_err(|e| { + ServerFnErrorErr::Deserialization(e.to_string()).into() + }) } fn try_into_stream( self, - ) -> Result< - impl Stream> + Send + 'static, - ServerFnError, - > { + ) -> Result> + Send + 'static, E> { Ok(self.into_body().into_data_stream().map(|chunk| { - chunk.map_err(|e| ServerFnError::Deserialization(e.to_string())) + chunk.map_err(|e| { + ServerFnErrorErr::Deserialization(e.to_string()).into() + }) })) } } diff --git a/server_fn/src/request/browser.rs b/server_fn/src/request/browser.rs index 550b898cdf..79d732bd35 100644 --- a/server_fn/src/request/browser.rs +++ b/server_fn/src/request/browser.rs @@ -1,5 +1,8 @@ use super::ClientReq; -use crate::{client::get_server_url, error::ServerFnError}; +use crate::{ + client::get_server_url, + error::{FromServerFnError, ServerFnErrorErr}, +}; use bytes::Bytes; use futures::{Stream, StreamExt}; pub use gloo_net::http::Request; @@ -83,7 +86,10 @@ fn abort_signal() -> (Option, Option) { (ctrl.map(|ctrl| AbortOnDrop(Some(ctrl))), signal) } -impl ClientReq for BrowserRequest { +impl ClientReq for BrowserRequest +where + E: FromServerFnError, +{ type FormData = BrowserFormData; fn try_new_get( @@ -91,7 +97,7 @@ impl ClientReq for BrowserRequest { accepts: &str, content_type: &str, query: &str, - ) -> Result> { + ) -> Result { let (abort_ctrl, abort_signal) = abort_signal(); let server_url = get_server_url(); let mut url = String::with_capacity( @@ -107,7 +113,9 @@ impl ClientReq for BrowserRequest { .header("Accept", accepts) .abort_signal(abort_signal.as_ref()) .build() - .map_err(|e| ServerFnError::Request(e.to_string()))?, + .map_err(|e| { + E::from(ServerFnErrorErr::Request(e.to_string())) + })?, abort_ctrl, }))) } @@ -117,7 +125,7 @@ impl ClientReq for BrowserRequest { accepts: &str, content_type: &str, body: String, - ) -> Result> { + ) -> Result { let (abort_ctrl, abort_signal) = abort_signal(); let server_url = get_server_url(); let mut url = String::with_capacity(server_url.len() + path.len()); @@ -129,7 +137,9 @@ impl ClientReq for BrowserRequest { .header("Accept", accepts) .abort_signal(abort_signal.as_ref()) .body(body) - .map_err(|e| ServerFnError::Request(e.to_string()))?, + .map_err(|e| { + E::from(ServerFnErrorErr::Request(e.to_string())) + })?, abort_ctrl, }))) } @@ -139,7 +149,7 @@ impl ClientReq for BrowserRequest { accepts: &str, content_type: &str, body: Bytes, - ) -> Result> { + ) -> Result { let (abort_ctrl, abort_signal) = abort_signal(); let server_url = get_server_url(); let mut url = String::with_capacity(server_url.len() + path.len()); @@ -153,7 +163,9 @@ impl ClientReq for BrowserRequest { .header("Accept", accepts) .abort_signal(abort_signal.as_ref()) .body(body) - .map_err(|e| ServerFnError::Request(e.to_string()))?, + .map_err(|e| { + E::from(ServerFnErrorErr::Request(e.to_string())) + })?, abort_ctrl, }))) } @@ -162,7 +174,7 @@ impl ClientReq for BrowserRequest { path: &str, accepts: &str, body: Self::FormData, - ) -> Result> { + ) -> Result { let (abort_ctrl, abort_signal) = abort_signal(); let server_url = get_server_url(); let mut url = String::with_capacity(server_url.len() + path.len()); @@ -173,7 +185,9 @@ impl ClientReq for BrowserRequest { .header("Accept", accepts) .abort_signal(abort_signal.as_ref()) .body(body.0.take()) - .map_err(|e| ServerFnError::Request(e.to_string()))?, + .map_err(|e| { + E::from(ServerFnErrorErr::Request(e.to_string())) + })?, abort_ctrl, }))) } @@ -183,17 +197,17 @@ impl ClientReq for BrowserRequest { accepts: &str, content_type: &str, body: Self::FormData, - ) -> Result> { + ) -> Result { let (abort_ctrl, abort_signal) = abort_signal(); let form_data = body.0.take(); let url_params = UrlSearchParams::new_with_str_sequence_sequence(&form_data) .map_err(|e| { - ServerFnError::Serialization(e.as_string().unwrap_or_else( - || { + E::from(ServerFnErrorErr::Serialization( + e.as_string().unwrap_or_else(|| { "Could not serialize FormData to URLSearchParams" .to_string() - }, + }), )) })?; Ok(Self(SendWrapper::new(RequestInner { @@ -202,7 +216,9 @@ impl ClientReq for BrowserRequest { .header("Accept", accepts) .abort_signal(abort_signal.as_ref()) .body(url_params) - .map_err(|e| ServerFnError::Request(e.to_string()))?, + .map_err(|e| { + E::from(ServerFnErrorErr::Request(e.to_string())) + })?, abort_ctrl, }))) } @@ -212,11 +228,12 @@ impl ClientReq for BrowserRequest { accepts: &str, content_type: &str, body: impl Stream + 'static, - ) -> Result> { + ) -> Result { // TODO abort signal let (request, abort_ctrl) = - streaming_request(path, accepts, content_type, body) - .map_err(|e| ServerFnError::Request(format!("{e:?}")))?; + streaming_request(path, accepts, content_type, body).map_err( + |e| E::from(ServerFnErrorErr::Request(format!("{e:?}"))), + )?; Ok(Self(SendWrapper::new(RequestInner { request, abort_ctrl, diff --git a/server_fn/src/request/generic.rs b/server_fn/src/request/generic.rs index da1add07ff..e8453e3dc8 100644 --- a/server_fn/src/request/generic.rs +++ b/server_fn/src/request/generic.rs @@ -12,7 +12,10 @@ //! * `wasm32-wasip*` integration crate `leptos_wasi` is using this //! crate under the hood. -use crate::request::Req; +use crate::{ + error::{FromServerFnError, ServerFnErrorErr}, + request::Req, +}; use bytes::Bytes; use futures::{ stream::{self, Stream}, @@ -21,30 +24,23 @@ use futures::{ use http::Request; use std::borrow::Cow; -impl Req for Request +impl Req for Request where - CustErr: 'static, + E: FromServerFnError, { - async fn try_into_bytes( - self, - ) -> Result> { + async fn try_into_bytes(self) -> Result { Ok(self.into_body()) } - async fn try_into_string( - self, - ) -> Result> { + async fn try_into_string(self) -> Result { String::from_utf8(self.into_body().into()).map_err(|err| { - crate::ServerFnError::Deserialization(err.to_string()) + ServerFnErrorErr::Deserialization(err.to_string()).into() }) } fn try_into_stream( self, - ) -> Result< - impl Stream> + Send + 'static, - crate::ServerFnError, - > { + ) -> Result> + Send + 'static, E> { Ok(stream::iter(self.into_body()) .ready_chunks(16) .map(|chunk| Ok(Bytes::from(chunk)))) diff --git a/server_fn/src/request/mod.rs b/server_fn/src/request/mod.rs index 3a4c71d393..f8340414c5 100644 --- a/server_fn/src/request/mod.rs +++ b/server_fn/src/request/mod.rs @@ -1,4 +1,3 @@ -use crate::error::ServerFnError; use bytes::Bytes; use futures::Stream; use std::{borrow::Cow, future::Future}; @@ -19,7 +18,7 @@ pub mod generic; pub mod reqwest; /// Represents a request as made by the client. -pub trait ClientReq +pub trait ClientReq where Self: Sized, { @@ -32,7 +31,7 @@ where content_type: &str, accepts: &str, query: &str, - ) -> Result>; + ) -> Result; /// Attempts to construct a new `POST` request with a text body. fn try_new_post( @@ -40,7 +39,7 @@ where content_type: &str, accepts: &str, body: String, - ) -> Result>; + ) -> Result; /// Attempts to construct a new `POST` request with a binary body. fn try_new_post_bytes( @@ -48,7 +47,7 @@ where content_type: &str, accepts: &str, body: Bytes, - ) -> Result>; + ) -> Result; /// Attempts to construct a new `POST` request with form data as the body. fn try_new_post_form_data( @@ -56,14 +55,14 @@ where accepts: &str, content_type: &str, body: Self::FormData, - ) -> Result>; + ) -> Result; /// Attempts to construct a new `POST` request with a multipart body. fn try_new_multipart( path: &str, accepts: &str, body: Self::FormData, - ) -> Result>; + ) -> Result; /// Attempts to construct a new `POST` request with a streaming body. fn try_new_streaming( @@ -71,11 +70,11 @@ where accepts: &str, content_type: &str, body: impl Stream + Send + 'static, - ) -> Result>; + ) -> Result; } /// Represents the request as received by the server. -pub trait Req +pub trait Req where Self: Sized, { @@ -92,32 +91,22 @@ where fn referer(&self) -> Option>; /// Attempts to extract the body of the request into [`Bytes`]. - fn try_into_bytes( - self, - ) -> impl Future>> + Send; + fn try_into_bytes(self) -> impl Future> + Send; /// Attempts to convert the body of the request into a string. - fn try_into_string( - self, - ) -> impl Future>> + Send; + fn try_into_string(self) -> impl Future> + Send; /// Attempts to convert the body of the request into a stream of bytes. fn try_into_stream( self, - ) -> Result< - impl Stream> + Send + 'static, - ServerFnError, - >; + ) -> Result> + Send + 'static, E>; } /// A mocked request type that can be used in place of the actual server request, /// when compiling for the browser. pub struct BrowserMockReq; -impl Req for BrowserMockReq -where - CustErr: 'static, -{ +impl Req for BrowserMockReq { fn as_query(&self) -> Option<&str> { unreachable!() } @@ -133,20 +122,17 @@ where fn referer(&self) -> Option> { unreachable!() } - async fn try_into_bytes(self) -> Result> { + async fn try_into_bytes(self) -> Result { unreachable!() } - async fn try_into_string(self) -> Result> { + async fn try_into_string(self) -> Result { unreachable!() } fn try_into_stream( self, - ) -> Result< - impl Stream> + Send, - ServerFnError, - > { + ) -> Result> + Send, E> { Ok(futures::stream::once(async { unreachable!() })) } } diff --git a/server_fn/src/request/reqwest.rs b/server_fn/src/request/reqwest.rs index 1352da2fc1..6159c65ee7 100644 --- a/server_fn/src/request/reqwest.rs +++ b/server_fn/src/request/reqwest.rs @@ -1,5 +1,8 @@ use super::ClientReq; -use crate::{client::get_server_url, error::ServerFnError}; +use crate::{ + client::get_server_url, + error::{FromServerFnError, ServerFnErrorErr}, +}; use bytes::Bytes; use futures::Stream; use once_cell::sync::Lazy; @@ -8,7 +11,10 @@ pub use reqwest::{multipart::Form, Client, Method, Request, Url}; pub(crate) static CLIENT: Lazy = Lazy::new(Client::new); -impl ClientReq for Request { +impl ClientReq for Request +where + E: FromServerFnError, +{ type FormData = Form; fn try_new_get( @@ -16,17 +22,17 @@ impl ClientReq for Request { accepts: &str, content_type: &str, query: &str, - ) -> Result> { + ) -> Result { let url = format!("{}{}", get_server_url(), path); let mut url = Url::try_from(url.as_str()) - .map_err(|e| ServerFnError::Request(e.to_string()))?; + .map_err(|e| E::from(ServerFnErrorErr::Request(e.to_string())))?; url.set_query(Some(query)); let req = CLIENT .get(url) .header(CONTENT_TYPE, content_type) .header(ACCEPT, accepts) .build() - .map_err(|e| ServerFnError::Request(e.to_string()))?; + .map_err(|e| E::from(ServerFnErrorErr::Request(e.to_string())))?; Ok(req) } @@ -35,7 +41,7 @@ impl ClientReq for Request { accepts: &str, content_type: &str, body: String, - ) -> Result> { + ) -> Result { let url = format!("{}{}", get_server_url(), path); CLIENT .post(url) @@ -43,7 +49,7 @@ impl ClientReq for Request { .header(ACCEPT, accepts) .body(body) .build() - .map_err(|e| ServerFnError::Request(e.to_string())) + .map_err(|e| ServerFnErrorErr::Request(e.to_string()).into()) } fn try_new_post_bytes( @@ -51,7 +57,7 @@ impl ClientReq for Request { accepts: &str, content_type: &str, body: Bytes, - ) -> Result> { + ) -> Result { let url = format!("{}{}", get_server_url(), path); CLIENT .post(url) @@ -59,20 +65,20 @@ impl ClientReq for Request { .header(ACCEPT, accepts) .body(body) .build() - .map_err(|e| ServerFnError::Request(e.to_string())) + .map_err(|e| ServerFnErrorErr::Request(e.to_string()).into()) } fn try_new_multipart( path: &str, accepts: &str, body: Self::FormData, - ) -> Result> { + ) -> Result { CLIENT .post(path) .header(ACCEPT, accepts) .multipart(body) .build() - .map_err(|e| ServerFnError::Request(e.to_string())) + .map_err(|e| ServerFnErrorErr::Request(e.to_string()).into()) } fn try_new_post_form_data( @@ -80,14 +86,14 @@ impl ClientReq for Request { accepts: &str, content_type: &str, body: Self::FormData, - ) -> Result> { + ) -> Result { CLIENT .post(path) .header(CONTENT_TYPE, content_type) .header(ACCEPT, accepts) .multipart(body) .build() - .map_err(|e| ServerFnError::Request(e.to_string())) + .map_err(|e| ServerFnErrorErr::Request(e.to_string()).into()) } fn try_new_streaming( @@ -95,7 +101,7 @@ impl ClientReq for Request { _accepts: &str, _content_type: &str, _body: impl Stream + 'static, - ) -> Result> { + ) -> Result { todo!("Streaming requests are not yet implemented for reqwest.") // We run into a fundamental issue here. // To be a reqwest body, the type must be Sync @@ -112,7 +118,7 @@ impl ClientReq for Request { .header(ACCEPT, accepts) .body(body) .build() - .map_err(|e| ServerFnError::Request(e.to_string())) + .map_err(|e| ServerFnErrorErr::Request(e.to_string()).into()) }*/ } } diff --git a/server_fn/src/request/spin.rs b/server_fn/src/request/spin.rs index 58781343d5..f819657a30 100644 --- a/server_fn/src/request/spin.rs +++ b/server_fn/src/request/spin.rs @@ -8,7 +8,7 @@ use http::{ use http_body_util::BodyExt; use std::borrow::Cow; -impl Req for IncomingRequest +impl Req for IncomingRequest where CustErr: 'static, { @@ -34,29 +34,31 @@ where .map(|h| String::from_utf8_lossy(h.as_bytes())) } - async fn try_into_bytes(self) -> Result> { + async fn try_into_bytes(self) -> Result { let (_parts, body) = self.into_parts(); - body.collect() - .await - .map(|c| c.to_bytes()) - .map_err(|e| ServerFnError::Deserialization(e.to_string())) + body.collect().await.map(|c| c.to_bytes()).map_err(|e| { + ServerFnErrorErr::Deserialization(e.to_string()).into() + }) } - async fn try_into_string(self) -> Result> { + async fn try_into_string(self) -> Result { let bytes = self.try_into_bytes().await?; - String::from_utf8(bytes.to_vec()) - .map_err(|e| ServerFnError::Deserialization(e.to_string())) + String::from_utf8(bytes.to_vec()).map_err(|e| { + ServerFnErrorErr::Deserialization(e.to_string()).into() + }) } fn try_into_stream( self, ) -> Result< impl Stream> + Send + 'static, - ServerFnError, + E, > { Ok(self.into_body().into_data_stream().map(|chunk| { - chunk.map_err(|e| ServerFnError::Deserialization(e.to_string())) + chunk.map_err(|e| { + ServerFnErrorErr::Deserialization(e.to_string()).into() + }) })) } } diff --git a/server_fn/src/response/actix.rs b/server_fn/src/response/actix.rs index 711268e717..8221877fd0 100644 --- a/server_fn/src/response/actix.rs +++ b/server_fn/src/response/actix.rs @@ -1,6 +1,6 @@ use super::Res; use crate::error::{ - ServerFnError, ServerFnErrorErr, ServerFnErrorSerde, SERVER_FN_ERROR_HEADER, + FromServerFnError, ServerFnErrorWrapper, SERVER_FN_ERROR_HEADER, }; use actix_web::{ http::{ @@ -13,10 +13,6 @@ use actix_web::{ use bytes::Bytes; use futures::{Stream, StreamExt}; use send_wrapper::SendWrapper; -use std::{ - fmt::{Debug, Display}, - str::FromStr, -}; /// A wrapped Actix response. /// @@ -38,14 +34,11 @@ impl From for ActixResponse { } } -impl Res for ActixResponse +impl Res for ActixResponse where - CustErr: FromStr + Display + Debug + 'static, + E: FromServerFnError, { - fn try_from_string( - content_type: &str, - data: String, - ) -> Result> { + fn try_from_string(content_type: &str, data: String) -> Result { let mut builder = HttpResponse::build(StatusCode::OK); Ok(ActixResponse(SendWrapper::new( builder @@ -54,10 +47,7 @@ where ))) } - fn try_from_bytes( - content_type: &str, - data: Bytes, - ) -> Result> { + fn try_from_bytes(content_type: &str, data: Bytes) -> Result { let mut builder = HttpResponse::build(StatusCode::OK); Ok(ActixResponse(SendWrapper::new( builder @@ -68,19 +58,17 @@ where fn try_from_stream( content_type: &str, - data: impl Stream>> + 'static, - ) -> Result> { + data: impl Stream> + 'static, + ) -> Result { let mut builder = HttpResponse::build(StatusCode::OK); Ok(ActixResponse(SendWrapper::new( builder .insert_header((header::CONTENT_TYPE, content_type)) - .streaming( - data.map(|data| data.map_err(ServerFnErrorErr::from)), - ), + .streaming(data.map(|data| data.map_err(ServerFnErrorWrapper))), ))) } - fn error_response(path: &str, err: &ServerFnError) -> Self { + fn error_response(path: &str, err: &E) -> Self { ActixResponse(SendWrapper::new( HttpResponse::build(StatusCode::INTERNAL_SERVER_ERROR) .append_header((SERVER_FN_ERROR_HEADER, path)) diff --git a/server_fn/src/response/browser.rs b/server_fn/src/response/browser.rs index 6c4cfcc1b5..f03750aaf8 100644 --- a/server_fn/src/response/browser.rs +++ b/server_fn/src/response/browser.rs @@ -1,5 +1,8 @@ use super::ClientRes; -use crate::{error::ServerFnError, redirect::REDIRECT_HEADER}; +use crate::{ + error::{FromServerFnError, ServerFnErrorErr}, + redirect::REDIRECT_HEADER, +}; use bytes::Bytes; use futures::{Stream, StreamExt}; pub use gloo_net::http::Response; @@ -12,48 +15,36 @@ use wasm_streams::ReadableStream; /// The response to a `fetch` request made in the browser. pub struct BrowserResponse(pub(crate) SendWrapper); -impl ClientRes for BrowserResponse { - fn try_into_string( - self, - ) -> impl Future>> + Send - { +impl ClientRes for BrowserResponse { + fn try_into_string(self) -> impl Future> + Send { // the browser won't send this async work between threads (because it's single-threaded) // so we can safely wrap this SendWrapper::new(async move { - self.0 - .text() - .await - .map_err(|e| ServerFnError::Deserialization(e.to_string())) + self.0.text().await.map_err(|e| { + ServerFnErrorErr::Deserialization(e.to_string()).into() + }) }) } - fn try_into_bytes( - self, - ) -> impl Future>> + Send - { + fn try_into_bytes(self) -> impl Future> + Send { // the browser won't send this async work between threads (because it's single-threaded) // so we can safely wrap this SendWrapper::new(async move { - self.0 - .binary() - .await - .map(Bytes::from) - .map_err(|e| ServerFnError::Deserialization(e.to_string())) + self.0.binary().await.map(Bytes::from).map_err(|e| { + ServerFnErrorErr::Deserialization(e.to_string()).into() + }) }) } fn try_into_stream( self, - ) -> Result< - impl Stream> + Send + 'static, - ServerFnError, - > { + ) -> Result> + Send + 'static, E> { let stream = ReadableStream::from_raw(self.0.body().unwrap()) .into_stream() .map(|data| match data { Err(e) => { web_sys::console::error_1(&e); - Err(ServerFnError::Request(format!("{e:?}"))) + Err(ServerFnErrorErr::Request(format!("{e:?}")).into()) } Ok(data) => { let data = data.unchecked_into::(); diff --git a/server_fn/src/response/generic.rs b/server_fn/src/response/generic.rs index f9e10b5f4c..644801481b 100644 --- a/server_fn/src/response/generic.rs +++ b/server_fn/src/response/generic.rs @@ -14,16 +14,13 @@ use super::Res; use crate::error::{ - ServerFnError, ServerFnErrorErr, ServerFnErrorSerde, SERVER_FN_ERROR_HEADER, + FromServerFnError, ServerFnErrorErr, ServerFnErrorWrapper, + SERVER_FN_ERROR_HEADER, }; use bytes::Bytes; use futures::{Stream, TryStreamExt}; use http::{header, HeaderValue, Response, StatusCode}; -use std::{ - fmt::{Debug, Display}, - pin::Pin, - str::FromStr, -}; +use std::pin::Pin; use throw_error::Error; /// The Body of a Response whose *execution model* can be @@ -44,51 +41,43 @@ impl From for Body { } } -impl Res for Response +impl Res for Response where - CustErr: Send + Sync + Debug + FromStr + Display + 'static, + E: Send + Sync + FromServerFnError, { - fn try_from_string( - content_type: &str, - data: String, - ) -> Result> { + fn try_from_string(content_type: &str, data: String) -> Result { let builder = http::Response::builder(); builder .status(200) .header(http::header::CONTENT_TYPE, content_type) .body(data.into()) - .map_err(|e| ServerFnError::Response(e.to_string())) + .map_err(|e| ServerFnErrorErr::Response(e.to_string()).into()) } - fn try_from_bytes( - content_type: &str, - data: Bytes, - ) -> Result> { + fn try_from_bytes(content_type: &str, data: Bytes) -> Result { let builder = http::Response::builder(); builder .status(200) .header(http::header::CONTENT_TYPE, content_type) .body(Body::Sync(data)) - .map_err(|e| ServerFnError::Response(e.to_string())) + .map_err(|e| ServerFnErrorErr::Response(e.to_string()).into()) } fn try_from_stream( content_type: &str, - data: impl Stream>> - + Send - + 'static, - ) -> Result> { + data: impl Stream> + Send + 'static, + ) -> Result { let builder = http::Response::builder(); builder .status(200) .header(http::header::CONTENT_TYPE, content_type) .body(Body::Async(Box::pin( - data.map_err(ServerFnErrorErr::from).map_err(Error::from), + data.map_err(ServerFnErrorWrapper).map_err(Error::from), ))) - .map_err(|e| ServerFnError::Response(e.to_string())) + .map_err(|e| ServerFnErrorErr::Response(e.to_string()).into()) } - fn error_response(path: &str, err: &ServerFnError) -> Self { + fn error_response(path: &str, err: &E) -> Self { Response::builder() .status(http::StatusCode::INTERNAL_SERVER_ERROR) .header(SERVER_FN_ERROR_HEADER, path) diff --git a/server_fn/src/response/http.rs b/server_fn/src/response/http.rs index e8117f75cc..254bf4e36b 100644 --- a/server_fn/src/response/http.rs +++ b/server_fn/src/response/http.rs @@ -1,61 +1,49 @@ use super::Res; use crate::error::{ - ServerFnError, ServerFnErrorErr, ServerFnErrorSerde, SERVER_FN_ERROR_HEADER, + FromServerFnError, ServerFnErrorErr, ServerFnErrorWrapper, + SERVER_FN_ERROR_HEADER, }; use axum::body::Body; use bytes::Bytes; -use futures::{Stream, StreamExt}; +use futures::{Stream, TryStreamExt}; use http::{header, HeaderValue, Response, StatusCode}; -use std::{ - fmt::{Debug, Display}, - str::FromStr, -}; -impl Res for Response +impl Res for Response where - CustErr: Send + Sync + Debug + FromStr + Display + 'static, + E: Send + Sync + FromServerFnError, { - fn try_from_string( - content_type: &str, - data: String, - ) -> Result> { + fn try_from_string(content_type: &str, data: String) -> Result { let builder = http::Response::builder(); builder .status(200) .header(http::header::CONTENT_TYPE, content_type) .body(Body::from(data)) - .map_err(|e| ServerFnError::Response(e.to_string())) + .map_err(|e| ServerFnErrorErr::Response(e.to_string()).into()) } - fn try_from_bytes( - content_type: &str, - data: Bytes, - ) -> Result> { + fn try_from_bytes(content_type: &str, data: Bytes) -> Result { let builder = http::Response::builder(); builder .status(200) .header(http::header::CONTENT_TYPE, content_type) .body(Body::from(data)) - .map_err(|e| ServerFnError::Response(e.to_string())) + .map_err(|e| ServerFnErrorErr::Response(e.to_string()).into()) } fn try_from_stream( content_type: &str, - data: impl Stream>> - + Send - + 'static, - ) -> Result> { - let body = - Body::from_stream(data.map(|n| n.map_err(ServerFnErrorErr::from))); + data: impl Stream> + Send + 'static, + ) -> Result { + let body = Body::from_stream(data.map_err(|e| ServerFnErrorWrapper(e))); let builder = http::Response::builder(); builder .status(200) .header(http::header::CONTENT_TYPE, content_type) .body(body) - .map_err(|e| ServerFnError::Response(e.to_string())) + .map_err(|e| E::from(ServerFnErrorErr::Response(e.to_string()))) } - fn error_response(path: &str, err: &ServerFnError) -> Self { + fn error_response(path: &str, err: &E) -> Self { Response::builder() .status(http::StatusCode::INTERNAL_SERVER_ERROR) .header(SERVER_FN_ERROR_HEADER, path) diff --git a/server_fn/src/response/mod.rs b/server_fn/src/response/mod.rs index 6a0f60bace..b9e212e1be 100644 --- a/server_fn/src/response/mod.rs +++ b/server_fn/src/response/mod.rs @@ -13,62 +13,46 @@ pub mod http; #[cfg(feature = "reqwest")] pub mod reqwest; -use crate::error::ServerFnError; use bytes::Bytes; use futures::Stream; use std::future::Future; /// Represents the response as created by the server; -pub trait Res +pub trait Res where Self: Sized, { /// Attempts to convert a UTF-8 string into an HTTP response. - fn try_from_string( - content_type: &str, - data: String, - ) -> Result>; + fn try_from_string(content_type: &str, data: String) -> Result; /// Attempts to convert a binary blob represented as bytes into an HTTP response. - fn try_from_bytes( - content_type: &str, - data: Bytes, - ) -> Result>; + fn try_from_bytes(content_type: &str, data: Bytes) -> Result; /// Attempts to convert a stream of bytes into an HTTP response. fn try_from_stream( content_type: &str, - data: impl Stream>> - + Send - + 'static, - ) -> Result>; + data: impl Stream> + Send + 'static, + ) -> Result; /// Converts an error into a response, with a `500` status code and the error text as its body. - fn error_response(path: &str, err: &ServerFnError) -> Self; + fn error_response(path: &str, err: &E) -> Self; /// Redirect the response by setting a 302 code and Location header. fn redirect(&mut self, path: &str); } /// Represents the response as received by the client. -pub trait ClientRes { +pub trait ClientRes { /// Attempts to extract a UTF-8 string from an HTTP response. - fn try_into_string( - self, - ) -> impl Future>> + Send; + fn try_into_string(self) -> impl Future> + Send; /// Attempts to extract a binary blob from an HTTP response. - fn try_into_bytes( - self, - ) -> impl Future>> + Send; + fn try_into_bytes(self) -> impl Future> + Send; /// Attempts to extract a binary stream from an HTTP response. fn try_into_stream( self, - ) -> Result< - impl Stream> + Send + Sync + 'static, - ServerFnError, - >; + ) -> Result> + Send + Sync + 'static, E>; /// HTTP status code of the response. fn status(&self) -> u16; @@ -91,29 +75,23 @@ pub trait ClientRes { /// server response type when compiling for the client. pub struct BrowserMockRes; -impl Res for BrowserMockRes { - fn try_from_string( - _content_type: &str, - _data: String, - ) -> Result> { +impl Res for BrowserMockRes { + fn try_from_string(_content_type: &str, _data: String) -> Result { unreachable!() } - fn try_from_bytes( - _content_type: &str, - _data: Bytes, - ) -> Result> { + fn try_from_bytes(_content_type: &str, _data: Bytes) -> Result { unreachable!() } - fn error_response(_path: &str, _err: &ServerFnError) -> Self { + fn error_response(_path: &str, _err: &E) -> Self { unreachable!() } fn try_from_stream( _content_type: &str, - _data: impl Stream>>, - ) -> Result> { + _data: impl Stream>, + ) -> Result { unreachable!() } diff --git a/server_fn/src/response/reqwest.rs b/server_fn/src/response/reqwest.rs index f60338e48d..79086ab0bf 100644 --- a/server_fn/src/response/reqwest.rs +++ b/server_fn/src/response/reqwest.rs @@ -1,31 +1,28 @@ use super::ClientRes; -use crate::error::ServerFnError; +use crate::error::{FromServerFnError, ServerFnErrorErr}; use bytes::Bytes; use futures::{Stream, TryStreamExt}; use reqwest::Response; -impl ClientRes for Response { - async fn try_into_string(self) -> Result> { - self.text() - .await - .map_err(|e| ServerFnError::Deserialization(e.to_string())) +impl ClientRes for Response { + async fn try_into_string(self) -> Result { + self.text().await.map_err(|e| { + ServerFnErrorErr::Deserialization(e.to_string()).into() + }) } - async fn try_into_bytes(self) -> Result> { - self.bytes() - .await - .map_err(|e| ServerFnError::Deserialization(e.to_string())) + async fn try_into_bytes(self) -> Result { + self.bytes().await.map_err(|e| { + ServerFnErrorErr::Deserialization(e.to_string()).into() + }) } fn try_into_stream( self, - ) -> Result< - impl Stream> + Send + 'static, - ServerFnError, - > { + ) -> Result> + Send + 'static, E> { Ok(self .bytes_stream() - .map_err(|e| ServerFnError::Response(e.to_string()))) + .map_err(|e| ServerFnErrorErr::Response(e.to_string()).into())) } fn status(&self) -> u16 {