Skip to content

Commit

Permalink
Bring back FromStr/Display serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
ryo33 committed Nov 22, 2024
1 parent 36cb1c1 commit 4ea8c8a
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 43 deletions.
5 changes: 1 addition & 4 deletions leptos_server/src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ use reactive_graph::{
owner::use_context,
traits::DefinedAt,
};
use server_fn::{
error::{FromServerFnError, ServerFnErrorSerde},
ServerFn,
};
use server_fn::{error::FromServerFnError, ServerFn};
use std::{ops::Deref, panic::Location, sync::Arc};

/// An error that can be caused by a server action.
Expand Down
136 changes: 101 additions & 35 deletions server_fn/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

use base64::{engine::general_purpose::URL_SAFE, Engine as _};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::{fmt, fmt::Display, str::FromStr};
use std::{
fmt::{self, Display, Write},
str::FromStr,
};
use thiserror::Error;
use throw_error::Error;
use url::Url;
Expand Down Expand Up @@ -246,19 +249,6 @@ where
}
}

#[doc(hidden)]
/// An extension trait for types that can be serialized and deserialized to a [`String`].
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<String, Self::Error>;

/// Deserializes the custom error type from a [`String`].
fn de(data: &str) -> Self;
}

impl<E> std::error::Error for ServerFnError<E>
where
E: std::error::Error + 'static,
Expand Down Expand Up @@ -318,7 +308,13 @@ pub enum ServerFnErrorErr {

impl<CustErr> FromServerFnError for ServerFnError<CustErr>
where
CustErr: std::fmt::Debug + Display + Serialize + DeserializeOwned + 'static,
CustErr: std::fmt::Debug
+ Display
+ Serialize
+ DeserializeOwned
+ 'static
+ FromStr
+ Display,
{
fn from_server_fn_error(value: ServerFnErrorErr) -> Self {
match value {
Expand All @@ -345,6 +341,74 @@ where
ServerFnErrorErr::Response(value) => ServerFnError::Response(value),
}
}

fn ser(&self) -> String {
let mut buf = String::new();
let result = match self {
ServerFnError::WrappedServerError(e) => {
write!(&mut buf, "WrappedServerFn|{e}")
}
ServerFnError::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) => {
write!(&mut buf, "ServerError|{e}")
}
ServerFnError::MiddlewareError(e) => {
write!(&mut buf, "MiddlewareError|{e}")
}
ServerFnError::Deserialization(e) => {
write!(&mut buf, "Deserialization|{e}")
}
ServerFnError::Serialization(e) => {
write!(&mut buf, "Serialization|{e}")
}
ServerFnError::Args(e) => write!(&mut buf, "Args|{e}"),
ServerFnError::MissingArg(e) => {
write!(&mut buf, "MissingArg|{e}")
}
};
match result {
Ok(()) => buf,
Err(_) => "Serialization|".to_string(),
}
}

fn de(data: &str) -> Self {
data.split_once('|')
.and_then(|(ty, data)| match ty {
"WrappedServerFn" => match CustErr::from_str(data) {
Ok(d) => Some(ServerFnError::WrappedServerError(d)),
Err(_) => None,
},
"Registration" => {
Some(ServerFnError::Registration(data.to_string()))
}
"Request" => Some(ServerFnError::Request(data.to_string())),
"Response" => Some(ServerFnError::Response(data.to_string())),
"ServerError" => {
Some(ServerFnError::ServerError(data.to_string()))
}
"Deserialization" => {
Some(ServerFnError::Deserialization(data.to_string()))
}
"Serialization" => {
Some(ServerFnError::Serialization(data.to_string()))
}
"Args" => Some(ServerFnError::Args(data.to_string())),
"MissingArg" => {
Some(ServerFnError::MissingArg(data.to_string()))
}
_ => None,
})
.unwrap_or_else(|| {
ServerFnError::Deserialization(format!(
"Could not deserialize error {data:?}"
))
})
}
}

/// Associates a particular server function error with the server function
Expand Down Expand Up @@ -384,10 +448,7 @@ impl<E: FromServerFnError> ServerFnUrlError<E> {
let mut url = Url::parse(base)?;
url.query_pairs_mut()
.append_pair("__path", &self.path)
.append_pair(
"__err",
&URL_SAFE.encode(self.error.ser().unwrap_or_default()),
);
.append_pair("__err", &URL_SAFE.encode(self.error.ser()));
Ok(url)
}

Expand Down Expand Up @@ -465,10 +526,30 @@ impl<E: FromServerFnError> std::error::Error for ServerFnErrorWrapper<E> {

/// A trait for types that can be returned from a server function.
pub trait FromServerFnError:
Display + std::fmt::Debug + ServerFnErrorSerde + 'static
Display + std::fmt::Debug + Serialize + DeserializeOwned + 'static
{
/// Converts a [`ServerFnErrorErr`] into the application-specific custom error type.
fn from_server_fn_error(value: ServerFnErrorErr) -> Self;

/// Converts the custom error type to a [`String`]. Defaults to serializing to JSON.
fn ser(&self) -> String {
serde_json::to_string(self).unwrap_or_else(|e| {
serde_json::to_string(&Self::from_server_fn_error(
ServerFnErrorErr::Serialization(e.to_string()),
))
.expect(
"error serializing should success at least with the \
Serialization error",
)
})
}

/// Deserializes the custom error type from a [`&str`]. Defaults to deserializing from JSON.
fn de(data: &str) -> Self {
serde_json::from_str(data).unwrap_or_else(|e| {
ServerFnErrorErr::Deserialization(e.to_string()).into_app_error()
})
}
}

/// A helper trait for converting a [`ServerFnErrorErr`] into an application-specific custom error type that implements [`FromServerFnError`].
Expand All @@ -492,18 +573,3 @@ fn assert_from_server_fn_error_impl() {

assert_impl::<ServerFnError>();
}

impl<E> ServerFnErrorSerde for E
where
E: Serialize + DeserializeOwned,
{
type Error = serde_json::Error;

fn ser(&self) -> Result<String, Self::Error> {
serde_json::to_string(self)
}

fn de(data: &str) -> Self {
serde_json::from_str(data).unwrap()
}
}
2 changes: 1 addition & 1 deletion server_fn/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,10 @@ use codec::{Encoding, FromReq, FromRes, IntoReq, IntoRes};
#[doc(hidden)]
pub use const_format;
use dashmap::DashMap;
use error::FromServerFnError;
pub use error::ServerFnError;
#[cfg(feature = "form-redirects")]
use error::ServerFnUrlError;
use error::{FromServerFnError, ServerFnErrorSerde};
use http::Method;
use middleware::{Layer, Service};
use once_cell::sync::Lazy;
Expand Down
2 changes: 1 addition & 1 deletion server_fn/src/response/actix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ where
ActixResponse(SendWrapper::new(
HttpResponse::build(StatusCode::INTERNAL_SERVER_ERROR)
.append_header((SERVER_FN_ERROR_HEADER, path))
.body(err.ser().unwrap_or_else(|_| err.to_string())),
.body(err.ser()),
))
}

Expand Down
2 changes: 1 addition & 1 deletion server_fn/src/response/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ where
Response::builder()
.status(http::StatusCode::INTERNAL_SERVER_ERROR)
.header(SERVER_FN_ERROR_HEADER, path)
.body(err.ser().unwrap_or_else(|_| err.to_string()).into())
.body(err.ser().into())
.unwrap()
}

Expand Down
2 changes: 1 addition & 1 deletion server_fn/src/response/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ where
Response::builder()
.status(http::StatusCode::INTERNAL_SERVER_ERROR)
.header(SERVER_FN_ERROR_HEADER, path)
.body(err.ser().unwrap_or_else(|_| err.to_string()).into())
.body(err.ser().into())
.unwrap()
}

Expand Down

0 comments on commit 4ea8c8a

Please sign in to comment.