From f6a3fb9c4d8dd86f78c9f75a23c1ac35bf35d4eb Mon Sep 17 00:00:00 2001 From: Benedikt Peetz Date: Thu, 19 Mar 2026 07:45:14 +0100 Subject: feat(treewide): Commit MVP --- rocie-macros/src/form/generate.rs | 136 +++++++++++++++++++++++++++++++++----- 1 file changed, 120 insertions(+), 16 deletions(-) (limited to 'rocie-macros/src/form/generate.rs') diff --git a/rocie-macros/src/form/generate.rs b/rocie-macros/src/form/generate.rs index 5642e6a..89acea8 100644 --- a/rocie-macros/src/form/generate.rs +++ b/rocie-macros/src/form/generate.rs @@ -1,7 +1,7 @@ use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use quote::{format_ident, quote}; -use syn::{Ident, Type, parse_macro_input}; +use syn::{Ident, Path, Type, TypePath, parse_macro_input, punctuated::Punctuated}; use crate::form::{ParsedChild, ParsedInput}; @@ -33,7 +33,9 @@ pub fn form_impl(item: TokenStream) -> TokenStream { let output = quote!({ use crate::components::{ input_placeholder::InputPlaceholder, - select_placeholder::SelectPlaceholder + select_placeholder::SelectPlaceholder, + textarea_placeholder::TextareaPlaceholder, + checkbox_placeholder::CheckboxPlaceholder, }; use leptos::{ @@ -48,7 +50,7 @@ pub fn form_impl(item: TokenStream) -> TokenStream { Set, Show, }, - html::{Input, Select}, + html::{Input, Select, Textarea}, web_sys::SubmitEvent }; @@ -126,6 +128,8 @@ fn node_ref(child: &ParsedChild) -> TokenStream2 { let (name, ty) = match child { ParsedChild::Input { name, .. } => (name, quote! {Input}), ParsedChild::Select { name, .. } => (name, quote! {Select}), + ParsedChild::Textarea { name, .. } => (name, quote! {Textarea}), + ParsedChild::Checkbox { name, .. } => (name, quote! {Input}), ParsedChild::Show { .. } => unreachable!("Filtered out before"), }; @@ -149,7 +153,9 @@ fn signal(child: &ParsedChild) -> TokenStream2 { let (#signal_name_get, #signal_name_set) = signal(None); } } - ParsedChild::Select { .. } => quote! {}, + ParsedChild::Textarea { .. } + | ParsedChild::Checkbox { .. } + | ParsedChild::Select { .. } => quote! {}, ParsedChild::Show { children, .. } => children.iter().map(signal).collect(), } } @@ -185,7 +191,9 @@ fn auto_complete_signal(child: &ParsedChild) -> TokenStream2 { quote! {} } } - ParsedChild::Select { .. } => quote! {}, + ParsedChild::Select { .. } + | ParsedChild::Textarea { .. } + | ParsedChild::Checkbox { .. } => quote! {}, ParsedChild::Show { children, .. } => children.iter().map(auto_complete_signal).collect(), } } @@ -238,14 +246,16 @@ fn reactive_signal(child: &ParsedChild) -> TokenStream2 { quote! {} } } - ParsedChild::Select { .. } => quote! {}, + ParsedChild::Select { .. } + | ParsedChild::Textarea { .. } + | ParsedChild::Checkbox { .. } => quote! {}, ParsedChild::Show { children, .. } => children.iter().map(reactive_signal).collect(), } } fn type_verification(child: &ParsedChild) -> TokenStream2 { match child { - ParsedChild::Input { .. } => { + ParsedChild::Input { .. } | ParsedChild::Textarea { .. } | ParsedChild::Checkbox { .. } => { quote! {} } ParsedChild::Select { name, options, .. } => { @@ -278,24 +288,65 @@ fn type_verification(child: &ParsedChild) -> TokenStream2 { fn get_names(input: &ParsedChild) -> Vec<&Ident> { match input { - ParsedChild::Input { name, .. } => vec![name], - ParsedChild::Select { name, .. } => vec![name], + ParsedChild::Input { name, .. } + | ParsedChild::Textarea { name, .. } + | ParsedChild::Checkbox { name, .. } + | ParsedChild::Select { name, .. } => vec![name], ParsedChild::Show { children, .. } => children.iter().flat_map(get_names).collect(), } } fn output_struct_definition(children: &[ParsedChild]) -> TokenStream2 { - fn get_rust_types(input: &ParsedChild) -> Vec<&Type> { + macro_rules! mk_type { + ($name:ident) => {{ + let segments = { + let mut p = Punctuated::new(); + p.push(syn::PathSegment { + ident: format_ident!(stringify!($name)), + arguments: syn::PathArguments::None, + }); + p + }; + + Type::Path(TypePath { + qself: None, + path: Path { + leading_colon: None, + segments, + }, + }) + }}; + } + + let string: Type = mk_type!(String); + let boolean: Type = mk_type!(bool); + + fn get_rust_types<'a>( + input: &'a ParsedChild, + string: &'a Type, + bool: &'a Type, + ) -> Vec<&'a Type> { match input { - ParsedChild::Input { rust_type, .. } => vec![rust_type], - ParsedChild::Select { rust_type, .. } => vec![rust_type], - ParsedChild::Show { children, .. } => { - children.iter().flat_map(get_rust_types).collect() + ParsedChild::Textarea { .. } => { + vec![string] } + ParsedChild::Checkbox { .. } => { + vec![bool] + } + ParsedChild::Input { rust_type, .. } | ParsedChild::Select { rust_type, .. } => { + vec![rust_type] + } + ParsedChild::Show { children, .. } => children + .iter() + .flat_map(|i| get_rust_types(i, string, bool)) + .collect(), } } + let names = children.iter().flat_map(get_names); - let rust_types = children.iter().flat_map(get_rust_types); + let rust_types = children + .iter() + .flat_map(|i| get_rust_types(i, &string, &boolean)); quote! { struct Inputs { @@ -358,6 +409,42 @@ fn fetch_value(child: &ParsedChild) -> TokenStream2 { }; } } + ParsedChild::Textarea { name, .. } => { + let node_ref_name = node_ref_name(name); + + quote! { + let #name: String = { + let output = #node_ref_name + .get() + // event handlers can only fire after the view + // is mounted to the DOM, so the `NodeRef` will be `Some` + .expect("