diff --git a/leptos_macro/src/lib.rs b/leptos_macro/src/lib.rs index 12cafd048..dc949643b 100644 --- a/leptos_macro/src/lib.rs +++ b/leptos_macro/src/lib.rs @@ -915,6 +915,16 @@ pub fn slot(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream { /// Whatever encoding is provided to `input` should implement `IntoReq` and `FromReq`. Whatever encoding is provided /// to `output` should implement `IntoRes` and `FromRes`. /// +/// ## Default Values for Parameters +/// +/// Individual function parameters can be annotated with `#[server(default)]`, which will pass +/// through `#[serde(default)]`. This is useful for the empty values of arguments with some +/// encodings. The URL encoding, for example, omits a field entirely if it is an empty `Vec<_>`, +/// but this causes a deserialization error: the correct solution is to add `#[server(default)]`. +/// ```rust,ignore +/// pub async fn with_default_value(#[server(default)] values: Vec) /* etc. */ +/// ``` +/// /// ## Important Notes /// - **Server functions must be `async`.** Even if the work being done inside the function body /// can run synchronously on the server, from the client’s perspective it involves an asynchronous diff --git a/server_fn_macro/src/lib.rs b/server_fn_macro/src/lib.rs index f80c8e5df..e17d489c8 100644 --- a/server_fn_macro/src/lib.rs +++ b/server_fn_macro/src/lib.rs @@ -55,6 +55,54 @@ pub fn server_macro_impl( } }); + let fields = body + .inputs + .iter_mut() + .map(|f| { + let typed_arg = match f { + FnArg::Receiver(_) => { + return Err(syn::Error::new( + f.span(), + "cannot use receiver types in server function macro", + )) + } + FnArg::Typed(t) => t, + }; + + // strip `mut`, which is allowed in fn args but not in struct fields + if let Pat::Ident(ident) = &mut *typed_arg.pat { + ident.mutability = None; + } + + // allow #[server(default)] on fields + let mut default = false; + let mut other_attrs = Vec::new(); + for attr in typed_arg.attrs.iter() { + if !attr.path().is_ident("server") { + other_attrs.push(attr.clone()); + continue; + } + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("default") && meta.input.is_empty() { + default = true; + Ok(()) + } else { + Err(meta.error( + "Unrecognized #[server] attribute, expected \ + #[server(default)]", + )) + } + })?; + } + typed_arg.attrs = other_attrs; + if default { + Ok(quote! { #[serde(default)] pub #typed_arg }) + } else { + Ok(quote! { pub #typed_arg }) + } + }) + .collect::>>()?; + let dummy = body.to_dummy_output(); let dummy_name = body.to_dummy_ident(); let args = syn::parse::(args.into())?; @@ -130,60 +178,11 @@ pub fn server_macro_impl( }; // build struct for type - let mut body = body; let fn_name = &body.ident; let fn_name_as_str = body.ident.to_string(); let vis = body.vis; let attrs = body.attrs; - let fields = body - .inputs - .iter_mut() - .map(|f| { - let typed_arg = match f { - FnArg::Receiver(_) => { - return Err(syn::Error::new( - f.span(), - "cannot use receiver types in server function macro", - )) - } - FnArg::Typed(t) => t, - }; - - // strip `mut`, which is allowed in fn args but not in struct fields - if let Pat::Ident(ident) = &mut *typed_arg.pat { - ident.mutability = None; - } - - // allow #[server(default)] on fields — TODO is this documented? - let mut default = false; - let mut other_attrs = Vec::new(); - for attr in typed_arg.attrs.iter() { - if !attr.path().is_ident("server") { - other_attrs.push(attr.clone()); - continue; - } - attr.parse_nested_meta(|meta| { - if meta.path.is_ident("default") && meta.input.is_empty() { - default = true; - Ok(()) - } else { - Err(meta.error( - "Unrecognized #[server] attribute, expected \ - #[server(default)]", - )) - } - })?; - } - typed_arg.attrs = other_attrs; - if default { - Ok(quote! { #[serde(default)] pub #typed_arg }) - } else { - Ok(quote! { pub #typed_arg }) - } - }) - .collect::>>()?; - let fn_args = body .inputs .iter()