Skip to content

Commit

Permalink
Add FromServerFnError trait to server_fn crate
Browse files Browse the repository at this point in the history
  • Loading branch information
ryo33 committed Nov 22, 2024
1 parent 89f26f6 commit e2b28a1
Show file tree
Hide file tree
Showing 31 changed files with 772 additions and 728 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 12 additions & 9 deletions leptos_server/src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -42,7 +45,7 @@ where
S: ServerFn + 'static,
S::Output: 'static,
{
inner: ArcAction<S, Result<S::Output, ServerFnError<S::Error>>>,
inner: ArcAction<S, Result<S::Output, S::Error>>,
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
}
Expand All @@ -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::<ServerActionError>().and_then(|error| {
(error.path() == S::PATH)
.then(|| ServerFnError::<S::Error>::de(error.err()))
.then(|| S::Error::de(error.err()))
.map(Err)
});
Self {
Expand All @@ -76,7 +80,7 @@ where
S: ServerFn + 'static,
S::Output: 'static,
{
type Target = ArcAction<S, Result<S::Output, ServerFnError<S::Error>>>;
type Target = ArcAction<S, Result<S::Output, S::Error>>;

fn deref(&self) -> &Self::Target {
&self.inner
Expand Down Expand Up @@ -131,7 +135,7 @@ where
S: ServerFn + 'static,
S::Output: 'static,
{
inner: Action<S, Result<S::Output, ServerFnError<S::Error>>>,
inner: Action<S, Result<S::Output, S::Error>>,
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
}
Expand All @@ -146,7 +150,7 @@ where
pub fn new() -> Self {
let err = use_context::<ServerActionError>().and_then(|error| {
(error.path() == S::PATH)
.then(|| ServerFnError::<S::Error>::de(error.err()))
.then(|| S::Error::de(error.err()))
.map(Err)
});
Self {
Expand Down Expand Up @@ -182,15 +186,14 @@ where
S::Output: Send + Sync + 'static,
S::Error: Send + Sync + 'static,
{
type Target = Action<S, Result<S::Output, ServerFnError<S::Error>>>;
type Target = Action<S, Result<S::Output, S::Error>>;

fn deref(&self) -> &Self::Target {
&self.inner
}
}

impl<S> From<ServerAction<S>>
for Action<S, Result<S::Output, ServerFnError<S::Error>>>
impl<S> From<ServerAction<S>> for Action<S, Result<S::Output, S::Error>>
where
S: ServerFn + 'static,
S::Output: 'static,
Expand Down
12 changes: 6 additions & 6 deletions leptos_server/src/multi_action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -11,7 +11,7 @@ where
S: ServerFn + 'static,
S::Output: 'static,
{
inner: ArcMultiAction<S, Result<S::Output, ServerFnError<S::Error>>>,
inner: ArcMultiAction<S, Result<S::Output, S::Error>>,
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
}
Expand Down Expand Up @@ -40,7 +40,7 @@ where
S: ServerFn + 'static,
S::Output: 'static,
{
type Target = ArcMultiAction<S, Result<S::Output, ServerFnError<S::Error>>>;
type Target = ArcMultiAction<S, Result<S::Output, S::Error>>;

fn deref(&self) -> &Self::Target {
&self.inner
Expand Down Expand Up @@ -95,13 +95,13 @@ where
S: ServerFn + 'static,
S::Output: 'static,
{
inner: MultiAction<S, Result<S::Output, ServerFnError<S::Error>>>,
inner: MultiAction<S, Result<S::Output, S::Error>>,
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
}

impl<S> From<ServerMultiAction<S>>
for MultiAction<S, Result<S::Output, ServerFnError<S::Error>>>
for MultiAction<S, Result<S::Output, S::Error>>
where
S: ServerFn + 'static,
S::Output: 'static,
Expand Down Expand Up @@ -152,7 +152,7 @@ where
S::Output: 'static,
S::Error: 'static,
{
type Target = MultiAction<S, Result<S::Output, ServerFnError<S::Error>>>;
type Target = MultiAction<S, Result<S::Output, S::Error>>;

fn deref(&self) -> &Self::Target {
&self.inner
Expand Down
1 change: 1 addition & 0 deletions server_fn/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
35 changes: 19 additions & 16 deletions server_fn/src/client.rs
Original file line number Diff line number Diff line change
@@ -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();
Expand All @@ -21,41 +21,40 @@ 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<CustErr> {
pub trait Client<E> {
/// The type of a request sent by this client.
type Request: ClientReq<CustErr> + Send;
type Request: ClientReq<E> + Send;
/// The type of a response received by this client.
type Response: ClientRes<CustErr> + Send;
type Response: ClientRes<E> + Send;

/// Sends the request and receives a response.
fn send(
req: Self::Request,
) -> impl Future<Output = Result<Self::Response, ServerFnError<CustErr>>> + Send;
) -> impl Future<Output = Result<Self::Response, E>> + Send;
}

#[cfg(feature = "browser")]
/// Implements [`Client`] for a `fetch` request in the browser.
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<CustErr> Client<CustErr> for BrowserClient {
impl<E: FromServerFnError> Client<E> for BrowserClient {
type Request = BrowserRequest;
type Response = BrowserResponse;

fn send(
req: Self::Request,
) -> impl Future<Output = Result<Self::Response, ServerFnError<CustErr>>>
+ Send {
) -> impl Future<Output = Result<Self::Response, E>> + Send {
SendWrapper::new(async move {
let req = req.0.take();
let RequestInner {
Expand All @@ -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
Expand All @@ -83,25 +84,27 @@ 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;

/// Implements [`Client`] for a request made by [`reqwest`].
pub struct ReqwestClient;

impl<CustErr> Client<CustErr> for ReqwestClient {
impl<E: FromServerFnError> Client<E> for ReqwestClient {
type Request = Request;
type Response = Response;

fn send(
req: Self::Request,
) -> impl Future<Output = Result<Self::Response, ServerFnError<CustErr>>>
+ Send {
) -> impl Future<Output = Result<Self::Response, E>> + Send {
CLIENT
.execute(req)
.map_err(|e| ServerFnError::Request(e.to_string()))
.map_err(|e| ServerFnErrorErr::Request(e.to_string()).into())
}
}
}
62 changes: 32 additions & 30 deletions server_fn/src/codec/cbor.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::{Encoding, FromReq, FromRes, IntoReq, IntoRes};
use crate::{
error::ServerFnError,
error::{FromServerFnError, ServerFnErrorErr},
request::{ClientReq, Req},
response::{ClientRes, Res},
};
Expand All @@ -16,19 +16,17 @@ impl Encoding for Cbor {
const METHOD: Method = Method::POST;
}

impl<CustErr, T, Request> IntoReq<Cbor, Request, CustErr> for T
impl<E, T, Request> IntoReq<Cbor, Request, E> for T
where
Request: ClientReq<CustErr>,
Request: ClientReq<E>,
T: Serialize + Send,
E: FromServerFnError,
{
fn into_req(
self,
path: &str,
accepts: &str,
) -> Result<Request, ServerFnError<CustErr>> {
fn into_req(self, path: &str, accepts: &str) -> Result<Request, E> {
let mut buffer: Vec<u8> = 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,
Expand All @@ -38,40 +36,44 @@ where
}
}

impl<CustErr, T, Request> FromReq<Cbor, Request, CustErr> for T
impl<E, T, Request> FromReq<Cbor, Request, E> for T
where
Request: Req<CustErr> + Send + 'static,
Request: Req<E> + Send + 'static,
T: DeserializeOwned,
E: FromServerFnError,
{
async fn from_req(req: Request) -> Result<Self, ServerFnError<CustErr>> {
async fn from_req(req: Request) -> Result<Self, E> {
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<CustErr, T, Response> IntoRes<Cbor, Response, CustErr> for T
impl<E, T, Response> IntoRes<Cbor, Response, E> for T
where
Response: Res<CustErr>,
Response: Res<E>,
T: Serialize + Send,
E: FromServerFnError,
{
async fn into_res(self) -> Result<Response, ServerFnError<CustErr>> {
async fn into_res(self) -> Result<Response, E> {
let mut buffer: Vec<u8> = 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<CustErr, T, Response> FromRes<Cbor, Response, CustErr> for T
impl<E, T, Response> FromRes<Cbor, Response, E> for T
where
Response: ClientRes<CustErr> + Send,
Response: ClientRes<E> + Send,
T: DeserializeOwned + Send,
E: FromServerFnError,
{
async fn from_res(res: Response) -> Result<Self, ServerFnError<CustErr>> {
async fn from_res(res: Response) -> Result<Self, E> {
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())))
}
}

Expand Down Expand Up @@ -114,20 +116,20 @@ where
<ResponseBody as HttpBody>::Data: Send ,
<RequestBody as HttpBody>::Data: Send ,
{
async fn from_req(req: http::Request<RequestBody>) -> Result<Self, ServerFnError<CustErr>> {
async fn from_req(req: http::Request<RequestBody>) -> Result<Self, ServerFnError<E>> {
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<http::Request<Body>, ServerFnError<CustErr>> {
async fn into_req(self) -> Result<http::Request<Body>, ServerFnError<E>> {
let mut buffer: Vec<u8> = Vec::new();
ciborium::ser::into_writer(&self, &mut buffer)?;
let req = http::Request::builder()
Expand All @@ -139,17 +141,17 @@ where
.body(Body::from(buffer))?;
Ok(req)
}
async fn from_res(res: http::Response<ResponseBody>) -> Result<Self, ServerFnError<CustErr>> {
async fn from_res(res: http::Response<ResponseBody>) -> Result<Self, ServerFnError<E>> {
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<Body> {
Expand Down
Loading

0 comments on commit e2b28a1

Please sign in to comment.