Skip to content

Commit

Permalink
fix: use #[server(default)] to pass use default values for a field
Browse files Browse the repository at this point in the history
  • Loading branch information
gbj committed Jan 26, 2024
1 parent ca3806e commit c1a7aba
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 48 deletions.
10 changes: 10 additions & 0 deletions leptos_macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u32>) /* 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
Expand Down
96 changes: 48 additions & 48 deletions server_fn_macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Result<Vec<_>>>()?;

let dummy = body.to_dummy_output();
let dummy_name = body.to_dummy_ident();
let args = syn::parse::<ServerFnArgs>(args.into())?;
Expand Down Expand Up @@ -136,54 +184,6 @@ pub fn server_macro_impl(
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::<Result<Vec<_>>>()?;

let fn_args = body
.inputs
.iter()
Expand Down

0 comments on commit c1a7aba

Please sign in to comment.