diff --git a/leptos_macro/src/lib.rs b/leptos_macro/src/lib.rs index dc949643b9..6beebf3212 100644 --- a/leptos_macro/src/lib.rs +++ b/leptos_macro/src/lib.rs @@ -883,6 +883,11 @@ pub fn slot(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream { /// - `"GetCbor"`: `GET` request with URL-encoded arguments and CBOR response /// - `req` and `res` specify the HTTP request and response types to be used on the server (these /// should usually only be necessary if you are integrating with a server other than Actix/Axum) +/// - `impl_from`: specifies whether to implement trait `From` for server function's type or not. +/// By default, if a server function only has one argument, the macro automatically implements the `From` trait +/// to convert from the argument type to the server function type, and vice versa, allowing you to convert +/// between them easily. Setting `impl_from` to `false` disables this, which can be necessary for argument types +/// for which this would create a conflicting implementation. (defaults to `true`) /// /// ```rust,ignore /// #[server( @@ -891,6 +896,7 @@ pub fn slot(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream { /// endpoint = "my_fn", /// input = Cbor, /// output = Json +/// impl_from = true /// )] /// pub async fn my_wacky_server_fn(input: Vec) -> Result { /// todo!() diff --git a/server_fn_macro/src/lib.rs b/server_fn_macro/src/lib.rs index 613987aa4e..8560d555b0 100644 --- a/server_fn_macro/src/lib.rs +++ b/server_fn_macro/src/lib.rs @@ -119,6 +119,7 @@ pub fn server_macro_impl( res_ty, client, custom_wrapper, + impl_from, } = args; let prefix = prefix.unwrap_or_else(|| Literal::string(default_path)); let fn_path = fn_path.unwrap_or_else(|| Literal::string("")); @@ -206,8 +207,11 @@ pub fn server_macro_impl( FnArg::Receiver(_) => None, FnArg::Typed(t) => Some((&t.pat, &t.ty)), }); - let from_impl = - (body.inputs.len() == 1 && first_field.is_some()).then(|| { + let impl_from = impl_from.map(|v| v.value).unwrap_or(true); + let from_impl = (body.inputs.len() == 1 + && first_field.is_some() + && impl_from) + .then(|| { let field = first_field.unwrap(); let (name, ty) = field; quote! { @@ -676,6 +680,7 @@ struct ServerFnArgs { client: Option, custom_wrapper: Option, builtin_encoding: bool, + impl_from: Option, } impl Parse for ServerFnArgs { @@ -693,6 +698,7 @@ impl Parse for ServerFnArgs { let mut res_ty: Option = None; let mut client: Option = None; let mut custom_wrapper: Option = None; + let mut impl_from: Option = None; let mut use_key_and_value = false; let mut arg_pos = 0; @@ -800,6 +806,14 @@ impl Parse for ServerFnArgs { )); } custom_wrapper = Some(stream.parse()?); + } else if key == "impl_from" { + if impl_from.is_some() { + return Err(syn::Error::new( + key.span(), + "keyword argument repeated: `impl_from`", + )); + } + impl_from = Some(stream.parse()?); } else { return Err(lookahead.error()); } @@ -895,6 +909,7 @@ impl Parse for ServerFnArgs { res_ty, client, custom_wrapper, + impl_from, }) } }