From 47c49ca6492e506038d76f4448f3887a31931e49 Mon Sep 17 00:00:00 2001 From: videobitva Date: Sun, 18 Feb 2024 01:27:49 +0200 Subject: [PATCH 1/5] Add `impl_into` argument --- leptos_macro/src/lib.rs | 4 ++++ server_fn_macro/src/lib.rs | 21 ++++++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/leptos_macro/src/lib.rs b/leptos_macro/src/lib.rs index dc949643b9..d12153a1ce 100644 --- a/leptos_macro/src/lib.rs +++ b/leptos_macro/src/lib.rs @@ -883,6 +883,9 @@ 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_into`: specifies whether to implement trait Into for server function's argument or not, +/// given that there is only one argument; on making server action dispatch, this allows to call `.into()` on the +/// server function argument, eliminating the need to fill server function’s type manually (defaults to `true`) /// /// ```rust,ignore /// #[server( @@ -891,6 +894,7 @@ pub fn slot(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream { /// endpoint = "my_fn", /// input = Cbor, /// output = Json +/// impl_into = 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..35664789ce 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: impl_into, } = 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_into = impl_into.map(|v| v.value).unwrap_or(true); + let from_impl = (body.inputs.len() == 1 + && first_field.is_some() + && impl_into) + .then(|| { let field = first_field.unwrap(); let (name, ty) = field; quote! { @@ -638,7 +642,7 @@ fn err_type(return_ty: &Type) -> Result> { { if let Some(segment) = pat.path.segments.last() { if segment.ident == "ServerFnError" { - let args = &segment.arguments; + let args = &pat.path.segments[0].arguments; match args { // Result PathArguments::None => return Ok(None), @@ -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_into: 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_into" { + if impl_into.is_some() { + return Err(syn::Error::new( + key.span(), + "keyword argument repeated: `impl_into`", + )); + } + impl_into = Some(stream.parse()?); } else { return Err(lookahead.error()); } @@ -895,6 +909,7 @@ impl Parse for ServerFnArgs { res_ty, client, custom_wrapper, + impl_from: impl_into, }) } } From 5f1fb067ce84f9d3ae285d67b4508594cba3bcdb Mon Sep 17 00:00:00 2001 From: videobitva Date: Sun, 18 Feb 2024 01:39:41 +0200 Subject: [PATCH 2/5] Add `impl_into` argument --- leptos_macro/src/lib.rs | 4 ++++ server_fn_macro/src/lib.rs | 21 ++++++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/leptos_macro/src/lib.rs b/leptos_macro/src/lib.rs index dc949643b9..d12153a1ce 100644 --- a/leptos_macro/src/lib.rs +++ b/leptos_macro/src/lib.rs @@ -883,6 +883,9 @@ 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_into`: specifies whether to implement trait Into for server function's argument or not, +/// given that there is only one argument; on making server action dispatch, this allows to call `.into()` on the +/// server function argument, eliminating the need to fill server function’s type manually (defaults to `true`) /// /// ```rust,ignore /// #[server( @@ -891,6 +894,7 @@ pub fn slot(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream { /// endpoint = "my_fn", /// input = Cbor, /// output = Json +/// impl_into = 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..35664789ce 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: impl_into, } = 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_into = impl_into.map(|v| v.value).unwrap_or(true); + let from_impl = (body.inputs.len() == 1 + && first_field.is_some() + && impl_into) + .then(|| { let field = first_field.unwrap(); let (name, ty) = field; quote! { @@ -638,7 +642,7 @@ fn err_type(return_ty: &Type) -> Result> { { if let Some(segment) = pat.path.segments.last() { if segment.ident == "ServerFnError" { - let args = &segment.arguments; + let args = &pat.path.segments[0].arguments; match args { // Result PathArguments::None => return Ok(None), @@ -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_into: 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_into" { + if impl_into.is_some() { + return Err(syn::Error::new( + key.span(), + "keyword argument repeated: `impl_into`", + )); + } + impl_into = Some(stream.parse()?); } else { return Err(lookahead.error()); } @@ -895,6 +909,7 @@ impl Parse for ServerFnArgs { res_ty, client, custom_wrapper, + impl_from: impl_into, }) } } From 3b10384d1d7c8723b2713cee1f25a17837c40ffd Mon Sep 17 00:00:00 2001 From: videobitva Date: Sun, 18 Feb 2024 01:46:29 +0200 Subject: [PATCH 3/5] Revert unneeded changes --- server_fn_macro/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_fn_macro/src/lib.rs b/server_fn_macro/src/lib.rs index 35664789ce..3531122421 100644 --- a/server_fn_macro/src/lib.rs +++ b/server_fn_macro/src/lib.rs @@ -642,7 +642,7 @@ fn err_type(return_ty: &Type) -> Result> { { if let Some(segment) = pat.path.segments.last() { if segment.ident == "ServerFnError" { - let args = &pat.path.segments[0].arguments; + let args = &segment.arguments; match args { // Result PathArguments::None => return Ok(None), From 37692aced05193f86deea3e2c388a46b5ddaa94a Mon Sep 17 00:00:00 2001 From: videobitva Date: Mon, 19 Feb 2024 21:51:54 +0200 Subject: [PATCH 4/5] Address review comments Rename `impl_into` back to be `impl_from` Rework docstring --- leptos_macro/src/lib.rs | 8 +++++--- server_fn_macro/src/lib.rs | 18 +++++++++--------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/leptos_macro/src/lib.rs b/leptos_macro/src/lib.rs index d12153a1ce..951c3ba19c 100644 --- a/leptos_macro/src/lib.rs +++ b/leptos_macro/src/lib.rs @@ -883,9 +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_into`: specifies whether to implement trait Into for server function's argument or not, -/// given that there is only one argument; on making server action dispatch, this allows to call `.into()` on the -/// server function argument, eliminating the need to fill server function’s type manually (defaults to `true`) +/// - `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( diff --git a/server_fn_macro/src/lib.rs b/server_fn_macro/src/lib.rs index 3531122421..8560d555b0 100644 --- a/server_fn_macro/src/lib.rs +++ b/server_fn_macro/src/lib.rs @@ -119,7 +119,7 @@ pub fn server_macro_impl( res_ty, client, custom_wrapper, - impl_from: impl_into, + impl_from, } = args; let prefix = prefix.unwrap_or_else(|| Literal::string(default_path)); let fn_path = fn_path.unwrap_or_else(|| Literal::string("")); @@ -207,10 +207,10 @@ pub fn server_macro_impl( FnArg::Receiver(_) => None, FnArg::Typed(t) => Some((&t.pat, &t.ty)), }); - let impl_into = impl_into.map(|v| v.value).unwrap_or(true); + let impl_from = impl_from.map(|v| v.value).unwrap_or(true); let from_impl = (body.inputs.len() == 1 && first_field.is_some() - && impl_into) + && impl_from) .then(|| { let field = first_field.unwrap(); let (name, ty) = field; @@ -698,7 +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_into: Option = None; + let mut impl_from: Option = None; let mut use_key_and_value = false; let mut arg_pos = 0; @@ -806,14 +806,14 @@ impl Parse for ServerFnArgs { )); } custom_wrapper = Some(stream.parse()?); - } else if key == "impl_into" { - if impl_into.is_some() { + } else if key == "impl_from" { + if impl_from.is_some() { return Err(syn::Error::new( key.span(), - "keyword argument repeated: `impl_into`", + "keyword argument repeated: `impl_from`", )); } - impl_into = Some(stream.parse()?); + impl_from = Some(stream.parse()?); } else { return Err(lookahead.error()); } @@ -909,7 +909,7 @@ impl Parse for ServerFnArgs { res_ty, client, custom_wrapper, - impl_from: impl_into, + impl_from, }) } } From c3ffdbeea7d9389c8677ef53a98badccae72635e Mon Sep 17 00:00:00 2001 From: videobitva Date: Mon, 19 Feb 2024 22:27:36 +0200 Subject: [PATCH 5/5] Fix typo in docstring --- leptos_macro/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/leptos_macro/src/lib.rs b/leptos_macro/src/lib.rs index 951c3ba19c..6beebf3212 100644 --- a/leptos_macro/src/lib.rs +++ b/leptos_macro/src/lib.rs @@ -896,7 +896,7 @@ pub fn slot(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream { /// endpoint = "my_fn", /// input = Cbor, /// output = Json -/// impl_into = true +/// impl_from = true /// )] /// pub async fn my_wacky_server_fn(input: Vec) -> Result { /// todo!()