diff --git a/server_fn/Cargo.toml b/server_fn/Cargo.toml index 6f54c9958b..0ac4889bee 100644 --- a/server_fn/Cargo.toml +++ b/server_fn/Cargo.toml @@ -54,6 +54,7 @@ rkyv = { version = "0.7", features = [ "uuid", "strict", ], optional = true } +rmp-serde = { version = "1.1", optional = true } # client gloo-net = { version = "0.5", optional = true } @@ -102,6 +103,7 @@ multipart = ["browser", "dep:multer"] url = ["dep:serde_qs"] cbor = ["dep:ciborium"] rkyv = ["dep:rkyv"] +msgpack = ["dep:rmp-serde"] default-tls = ["reqwest?/default-tls"] rustls = ["reqwest?/rustls-tls"] reqwest = ["dep:reqwest"] diff --git a/server_fn/src/codec/mod.rs b/server_fn/src/codec/mod.rs index cfded1d792..b9a157a451 100644 --- a/server_fn/src/codec/mod.rs +++ b/server_fn/src/codec/mod.rs @@ -44,6 +44,11 @@ mod multipart; #[cfg(feature = "multipart")] pub use multipart::*; +#[cfg(feature = "msgpack")] +mod msgpack; +#[cfg(feature = "msgpack")] +pub use msgpack::*; + mod stream; use crate::error::ServerFnError; use futures::Future; diff --git a/server_fn/src/codec/msgpack.rs b/server_fn/src/codec/msgpack.rs new file mode 100644 index 0000000000..c06789e1a6 --- /dev/null +++ b/server_fn/src/codec/msgpack.rs @@ -0,0 +1,74 @@ +use super::{Encoding, FromReq, FromRes, IntoReq, IntoRes}; +use crate::{ + error::ServerFnError, + request::{ClientReq, Req}, + response::{ClientRes, Res}, +}; +use bytes::Bytes; +use http::Method; +use serde::{de::DeserializeOwned, Serialize}; + +/// A codec for MessagePack. +pub struct MsgPack; + +impl Encoding for MsgPack { + const CONTENT_TYPE: &'static str = "application/msgpack"; + const METHOD: Method = Method::POST; +} + +impl IntoReq for T +where + Request: ClientReq, + T: Serialize, +{ + fn into_req( + self, + path: &str, + accepts: &str, + ) -> Result> { + let data = rmp_serde::to_vec(&self) + .map_err(|e| ServerFnError::Serialization(e.to_string()))?; + Request::try_new_post_bytes( + path, + MsgPack::CONTENT_TYPE, + accepts, + Bytes::from(data), + ) + } +} + +impl FromReq for T +where + Request: Req + Send, + T: DeserializeOwned, +{ + 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())) + } +} + +impl IntoRes for T +where + Response: Res, + T: Serialize + Send, +{ + async fn into_res(self) -> Result> { + let data = rmp_serde::to_vec(&self) + .map_err(|e| ServerFnError::Serialization(e.to_string()))?; + Response::try_from_bytes(MsgPack::CONTENT_TYPE, Bytes::from(data)) + } +} + +impl FromRes for T +where + Response: ClientRes + Send, + T: DeserializeOwned, +{ + 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())) + } +}