From ae008631f6ac2a84ab7ebc0468a2d8d63e25670c Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sun, 17 Sep 2023 19:56:09 +1000 Subject: [PATCH 1/2] Allow for auto-generated props --- rscx-macros/src/lib.rs | 43 ++++++++++++++++++++++++++-- rscx/examples/autogenerated_props.rs | 38 ++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 rscx/examples/autogenerated_props.rs diff --git a/rscx-macros/src/lib.rs b/rscx-macros/src/lib.rs index 6e9341f..b6da0cf 100644 --- a/rscx-macros/src/lib.rs +++ b/rscx-macros/src/lib.rs @@ -8,7 +8,8 @@ use rstml::{ node::{KeyedAttribute, Node, NodeAttribute, NodeElement, NodeName}, Parser, ParserConfig, }; -use syn::{parse::Parse, spanned::Spanned, Expr, ExprLit, ItemStruct}; +use syn::punctuated::Punctuated; +use syn::{parse::Parse, parse_quote, spanned::Spanned, Expr, ExprLit, FnArg, ItemStruct, Token}; #[proc_macro] pub fn html(tokens: TokenStream) -> TokenStream { @@ -438,9 +439,45 @@ impl ToTokens for ComponentFn { let props = item.sig.inputs.first().unwrap(); (quote! {}, props.to_token_stream()) } - // TODO: generate #nameProps here _ => { - panic!("wrong props") + let field_defs = &item + .sig + .inputs + .clone() + .into_iter() + .map(|i| match i { + FnArg::Receiver(_) => { + panic!("receiver arguments unsupported"); + } + FnArg::Typed(mut t) => { + t.attrs.push(parse_quote! { #[builder(setter(into))] }); + t + } + }) + .collect::>(); + let field_names = item + .sig + .inputs + .iter() + .map(|i| match i { + FnArg::Receiver(_) => { + panic!("receiver arguments unsupported"); + } + FnArg::Typed(t) => &t.pat, + }) + .collect::>(); + let props_name = + syn::Ident::new(&format!("{}Props", name), proc_macro2::Span::call_site()); + + ( + quote! { + #[rscx::props] + pub struct #props_name { + #field_defs + } + }, + quote! { #props_name { #field_names }: #props_name }, + ) } }; diff --git a/rscx/examples/autogenerated_props.rs b/rscx/examples/autogenerated_props.rs new file mode 100644 index 0000000..e1e40e6 --- /dev/null +++ b/rscx/examples/autogenerated_props.rs @@ -0,0 +1,38 @@ +use rscx::{component, html}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let app = app().await; + println!("{}", app); + Ok(()) +} + +// simple function returning a String +async fn app() -> String { + let s = "ul { color: red; }"; + html! { + + + + + + + // call a component with props and children +
+

"I am a paragraph"

+
+ + + } +} + +#[component] +/// mark functions with #[component] to use them as components inside html! macro +fn Section(title: String, children: String) -> String { + html! { +
+

{ title }

+ { children } +
+ } +} From 175763dc299a84cae1f3d55411a91c2b671c708f Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sun, 17 Sep 2023 19:58:31 +1000 Subject: [PATCH 2/2] Only apply default if there are no attributes --- rscx-macros/src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rscx-macros/src/lib.rs b/rscx-macros/src/lib.rs index b6da0cf..26602ed 100644 --- a/rscx-macros/src/lib.rs +++ b/rscx-macros/src/lib.rs @@ -450,7 +450,10 @@ impl ToTokens for ComponentFn { panic!("receiver arguments unsupported"); } FnArg::Typed(mut t) => { - t.attrs.push(parse_quote! { #[builder(setter(into))] }); + if t.attrs.is_empty() { + t.attrs.push(parse_quote! { #[builder(setter(into))] }); + } + t } })