Skip to content

Commit

Permalink
feat: add serde-lite codec for server functions (#2168)
Browse files Browse the repository at this point in the history
  • Loading branch information
rakshith-ravi authored Jan 9, 2024
1 parent d8514c1 commit b84ff68
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 20 deletions.
2 changes: 1 addition & 1 deletion leptos/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ nightly = [
"leptos_server/nightly",
]
serde = ["leptos_reactive/serde"]
serde-lite = ["leptos_reactive/serde-lite"]
serde-lite = ["leptos_reactive/serde-lite", "server_fn/serde-lite"]
miniserde = ["leptos_reactive/miniserde"]
rkyv = ["leptos_reactive/rkyv"]
tracing = ["leptos_macro/tracing"]
Expand Down
4 changes: 2 additions & 2 deletions leptos_reactive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ description = "Reactive system for the Leptos web framework."
[dependencies]
slotmap = { version = "1", features = ["serde"] }
serde = { version = "1", features = ["derive"] }
serde-lite = { version = "0.4", optional = true }
serde-lite = { version = "0.5", optional = true }
futures = { version = "0.3" }
js-sys = { version = "0.3", optional = true }
miniserde = { version = "0.1", optional = true }
Expand All @@ -24,7 +24,7 @@ bytecheck = { version = "0.7", features = [
"simdutf8",
], optional = true }
rustc-hash = "1"
serde-wasm-bindgen = "0.5"
serde-wasm-bindgen = "0.6"
serde_json = "1"
spin-sdk = { git = "https://github.com/fermyon/spin", tag = "v2.0.1", optional = true }
base64 = "0.21"
Expand Down
2 changes: 2 additions & 0 deletions server_fn/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ multer = { version = "3", optional = true }
## output encodings
# serde
serde_json = "1"
serde-lite = { version = "0.5", features = ["derive"], optional = true }
futures = "0.3"
http = { version = "1" }
ciborium = { version = "0.2", optional = true }
Expand Down Expand Up @@ -83,6 +84,7 @@ browser = [
"dep:wasm-bindgen-futures",
]
json = []
serde-lite = ["dep:serde-lite"]
multipart = ["dep:multer"]
url = ["dep:serde_qs"]
cbor = ["dep:ciborium"]
Expand Down
14 changes: 11 additions & 3 deletions server_fn/src/codec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,24 @@
mod cbor;
#[cfg(feature = "cbor")]
pub use cbor::*;

#[cfg(feature = "json")]
mod json;
use http::Method;
#[cfg(feature = "json")]
pub use json::*;

#[cfg(feature = "serde-lite")]
mod serde_lite;
#[cfg(feature = "serde-lite")]
pub use serde_lite::*;

#[cfg(feature = "rkyv")]
mod rkyv;
#[cfg(feature = "rkyv")]
pub use rkyv::*;

#[cfg(feature = "url")]
mod url;
use crate::{error::ServerFnError, request::ClientReq};
use futures::Future;
#[cfg(feature = "url")]
pub use url::*;

Expand All @@ -24,6 +29,9 @@ mod multipart;
pub use multipart::*;

mod stream;
use crate::{error::ServerFnError, request::ClientReq};
use futures::Future;
use http::Method;
pub use stream::*;

pub trait FromReq<CustErr, Request, Encoding>
Expand Down
82 changes: 82 additions & 0 deletions server_fn/src/codec/serde_lite.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use super::{Encoding, FromReq, FromRes};
use crate::{
error::ServerFnError,
request::{ClientReq, Req},
response::{ClientRes, Res},
IntoReq, IntoRes,
};
use http::Method;
use serde_lite::{Deserialize, Serialize};
/// Pass arguments and receive responses as JSON in the body of a `POST` request.
pub struct SerdeLite;

impl Encoding for SerdeLite {
const CONTENT_TYPE: &'static str = "application/json";
const METHOD: Method = Method::POST;
}

impl<CustErr, T, Request> IntoReq<CustErr, Request, SerdeLite> for T
where
Request: ClientReq<CustErr>,
T: Serialize + Send,
{
fn into_req(
self,
path: &str,
accepts: &str,
) -> Result<Request, ServerFnError<CustErr>> {
let data = serde_json::to_string(
&self
.serialize()
.map_err(|e| ServerFnError::Serialization(e.to_string()))?,
)
.map_err(|e| ServerFnError::Serialization(e.to_string()))?;
Request::try_new_post(path, accepts, SerdeLite::CONTENT_TYPE, data)
}
}

impl<CustErr, T, Request> FromReq<CustErr, Request, SerdeLite> for T
where
Request: Req<CustErr> + Send + 'static,
T: Deserialize,
{
async fn from_req(req: Request) -> Result<Self, ServerFnError<CustErr>> {
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| ServerFnError::Args(e.to_string()))
}
}

impl<CustErr, T, Response> IntoRes<CustErr, Response, SerdeLite> for T
where
Response: Res<CustErr>,
T: Serialize + Send,
{
async fn into_res(self) -> Result<Response, ServerFnError<CustErr>> {
let data = serde_json::to_string(
&self
.serialize()
.map_err(|e| ServerFnError::Serialization(e.to_string()))?,
)
.map_err(|e| ServerFnError::Serialization(e.to_string()))?;
Response::try_from_string(SerdeLite::CONTENT_TYPE, data)
}
}

impl<CustErr, T, Response> FromRes<CustErr, Response, SerdeLite> for T
where
Response: ClientRes<CustErr> + Send,
T: Deserialize + Send,
{
async fn from_res(res: Response) -> Result<Self, ServerFnError<CustErr>> {
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| ServerFnError::Deserialization(e.to_string()))
}
}
3 changes: 3 additions & 0 deletions server_fn/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ use request::Req;
use response::{ClientRes, Res};
#[doc(hidden)]
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};
#[doc(hidden)]
pub use xxhash_rust;
Expand Down
32 changes: 18 additions & 14 deletions server_fn_macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,8 @@ pub fn server_macro_impl(
} = args;
let prefix = prefix.unwrap_or_else(|| Literal::string(default_path));
let fn_path = fn_path.unwrap_or_else(|| Literal::string(""));
let input = input.unwrap_or_else(|| syn::parse_quote!(PostUrl));
let input_is_rkyv = input == "Rkyv";
let input_is_multipart = input == "MultipartFormData";
let input = codec_ident(server_fn_path.as_ref(), input);
let input_ident = input.unwrap_or_else(|| syn::parse_quote!(PostUrl));
let input = codec_ident(server_fn_path.as_ref(), input_ident.clone());
let output = output.unwrap_or_else(|| syn::parse_quote!(Json));
let output = codec_ident(server_fn_path.as_ref(), output);
// default to PascalCase version of function name if no struct name given
Expand Down Expand Up @@ -309,17 +307,23 @@ pub fn server_macro_impl(
}
};

// TODO rkyv derives
let derives = if input_is_multipart {
quote! {}
} else if input_is_rkyv {
todo!("implement derives for Rkyv")
} else {
quote! {
Clone, #server_fn_path::serde::Serialize, #server_fn_path::serde::Deserialize
}
let (is_serde, derives) = match input_ident.to_string().as_str() {
"Rkyv" => todo!("implement derives for Rkyv"),
"MultipartFormData" => (false, quote! {}),
"SerdeLite" => (
true,
quote! {
Clone, #server_fn_path::serde_lite::Serialize, #server_fn_path::serde_lite::Deserialize
},
),
_ => (
true,
quote! {
Clone, #server_fn_path::serde::Serialize, #server_fn_path::serde::Deserialize
},
),
};
let serde_path = (!input_is_multipart && !input_is_rkyv).then(|| {
let serde_path = is_serde.then(|| {
quote! {
#[serde(crate = #serde_path)]
}
Expand Down

0 comments on commit b84ff68

Please sign in to comment.