Switch the default server function codec to json (#3602)

* Switch the default server function codec to json

* test both input and output encoding
This commit is contained in:
Evan Almloff authored and GitHub committed 2025-03-04 14:20:56 -06:00
1 parent 9967118784
commit eccd75a1fe
2 files changed
+115 -1

No files matched your search

@@ -41,6 +41,7 @@ fn app() -> Element {
Errors {}
}
OnMounted {}
DefaultServerFnCodec {}
DocumentElements {}
}
}
@@ -59,6 +60,15 @@ fn OnMounted() -> Element {
}
}
#[component]
fn DefaultServerFnCodec() -> Element {
let resource = use_server_future(|| get_server_data_empty_vec(Vec::new()))?;
let empty_vec = resource.unwrap().unwrap();
assert!(empty_vec.is_empty());
rsx! {}
}
#[cfg(feature = "server")]
async fn assert_server_context_provided() {
let FromContext(i): FromContext<u32> = extract().await.unwrap();
@@ -79,6 +89,15 @@ async fn get_server_data() -> Result<String, ServerFnError> {
Ok("Hello from the server!".to_string())
}
// Make sure the default codec work with empty data structures
// Regression test for https://github.com/DioxusLabs/dioxus/issues/2628
#[server]
async fn get_server_data_empty_vec(empty_vec: Vec<String>) -> Result<Vec<String>, ServerFnError> {
assert_server_context_provided().await;
assert!(empty_vec.is_empty());
Ok(Vec::new())
}
#[server]
async fn server_error() -> Result<String, ServerFnError> {
assert_server_context_provided().await;
+96 -1
View File
@@ -6,8 +6,12 @@
//! See the [server_fn_macro] crate for more information.
use proc_macro::TokenStream;
use quote::quote;
use server_fn_macro::server_macro_impl;
use syn::__private::ToTokens;
use syn::{
__private::ToTokens,
parse::{Parse, ParseStream},
};
/// Declares that a function is a [server function](https://docs.rs/server_fn/).
/// This means that its body will only run on the server, i.e., when the `ssr`
@@ -143,6 +147,9 @@ use syn::__private::ToTokens;
/// ```
#[proc_macro_attribute]
pub fn server(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
// If there is no input codec, use json as the default
let args = default_json_codec(args);
match server_macro_impl(
args.into(),
s.into(),
@@ -155,3 +162,91 @@ pub fn server(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
Ok(s) => s.to_token_stream().into(),
}
}
fn default_json_codec(args: TokenStream) -> TokenStream {
// Try to parse the args
if let Err(err) = syn::parse::<ServerFnArgsMetadata>(args.clone()) {
return err.to_compile_error().into();
}
let Ok(args_metadata) = syn::parse::<ServerFnArgsMetadata>(args.clone()) else {
// If we fail to parse the args, forward them directly to the macro for diagnostics
return args;
};
let mut new_tokens = args;
// Make sure the args always end with a comma
if args_metadata.requires_trailing_comma {
let default_comma: TokenStream = quote! {,}.into();
new_tokens.extend(default_comma);
}
// If the user didn't override the input codec, default to Json
if !args_metadata.manual_input {
let default_input: TokenStream = quote! {
input = server_fn::codec::Json,
}
.into();
new_tokens.extend(default_input);
}
// If the user didn't override the output codec, default to Json
if !args_metadata.manual_output {
let default_output: TokenStream = quote! {
output = server_fn::codec::Json,
}
.into();
new_tokens.extend(default_output);
}
new_tokens
}
struct ServerFnArgsMetadata {
manual_input: bool,
manual_output: bool,
requires_trailing_comma: bool,
}
impl Parse for ServerFnArgsMetadata {
fn parse(input: ParseStream) -> syn::Result<Self> {
let mut manual_input = false;
let mut manual_output = false;
let mut requires_trailing_comma = false;
let mut take_comma = |input: &ParseStream| {
let comma: Option<syn::Token![,]> = input.parse().ok();
requires_trailing_comma = comma.is_none();
};
while !input.is_empty() {
// Ignore legacy ident and string args
if input.peek(syn::Ident) && !input.peek2(syn::Token![=]) {
input.parse::<syn::Ident>()?;
take_comma(&input);
continue;
}
if input.peek(syn::LitStr) && !input.peek2(syn::Token![=]) {
input.parse::<syn::LitStr>()?;
take_comma(&input);
continue;
}
let ident: syn::Ident = input.parse()?;
let _: syn::Token![=] = input.parse()?;
let _: syn::Expr = input.parse()?;
if ident == "input" {
manual_input = true;
} else if ident == "output" {
manual_output = true;
}
take_comma(&input);
}
Ok(Self {
manual_input,
manual_output,
requires_trailing_comma,
})
}
}