use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use quote::{format_ident, quote}; use syn::{Ident, Path, Type, TypePath, parse_macro_input, punctuated::Punctuated}; use crate::form::{ParsedChild, ParsedInput}; pub fn form_impl(item: TokenStream) -> TokenStream { let input = parse_macro_input!(item as ParsedInput); let node_refs = input.children.iter().map(node_ref); let signals = input.children.iter().map(signal); let reactive_signals = input.children.iter().map(reactive_signal); let auto_complete_signals = input.children.iter().map(auto_complete_signal); let type_verifications = input.children.iter().map(type_verification); let output_struct_definition = output_struct_definition(&input.children); let output_struct_construction = output_struct_construction(&input.children); let fetch_values = input.children.iter().map(fetch_value); let bounds = input.on_submit.inputs; let on_submit_block = input.on_submit.block; let on_submit_move = if input.on_submit.should_use_move { quote! {move} } else { quote! {} }; let input_htmls = input.children.iter().map(input_html); let output = quote!({ use crate::components::{ input_placeholder::InputPlaceholder, select_placeholder::SelectPlaceholder, textarea_placeholder::TextareaPlaceholder, checkbox_placeholder::CheckboxPlaceholder, }; use leptos::{ view, prelude::{ Get, NodeRef, ElementChild, ClassAttribute, OnAttribute, signal, Set, Show, }, html::{Input, Select, Textarea}, web_sys::SubmitEvent }; use log::{info, error}; #( #node_refs #signals #reactive_signals #auto_complete_signals #type_verifications )* let on_submit = move |ev: SubmitEvent| { #output_struct_definition // stop the page from reloading! ev.prevent_default(); #( #fetch_values )* let real_on_submit = #on_submit_move |Inputs {#(#bounds),*}| #on_submit_block; real_on_submit( #output_struct_construction ) }; view! {
} }); // { // match syn::parse_file(quote! {fn main() { #output }}.to_string().as_str()) { // Ok(tree) => { // let formatted = prettyplease::unparse(&tree); // eprint!("{}", formatted); // } // Err(err) => { // eprintln!("Error: {err}\n{output}"); // } // }; // } output.into() } fn node_ref_name(name: &Ident) -> Ident { format_ident!("{name}_node_ref") } fn node_ref(child: &ParsedChild) -> TokenStream2 { if let ParsedChild::Show { children, .. } = child { children.iter().map(node_ref).collect() } else { 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"), }; let node_ref_name = node_ref_name(name); quote! { let #node_ref_name: NodeRef<#ty> = NodeRef::new(); } } } fn signal_names(name: &Ident) -> (Ident, Ident) { (format_ident!("{name}_get"), format_ident!("{name}_set")) } fn signal(child: &ParsedChild) -> TokenStream2 { match child { ParsedChild::Input { name, .. } => { let (signal_name_get, signal_name_set) = signal_names(name); quote! { let (#signal_name_get, #signal_name_set) = signal(None); } } ParsedChild::Textarea { .. } | ParsedChild::Checkbox { .. } | ParsedChild::Select { .. } => quote! {}, ParsedChild::Show { children, .. } => children.iter().map(signal).collect(), } } fn signal_auto_complete_names(name: &Ident) -> (Ident, Ident) { ( format_ident!("{name}_auto_complete_get"), format_ident!("{name}_auto_complete_set"), ) } fn auto_complete_signal(child: &ParsedChild) -> TokenStream2 { match child { ParsedChild::Input { name, reactive, auto_complete, .. } => { if let Some(auto_complete) = auto_complete { let (signal_auto_complete_get, _) = signal_auto_complete_names(name); let reactive = reactive .as_ref() .expect("Must be some, if auto_complete is some"); quote! { let #signal_auto_complete_get = leptos::prelude::LocalResource::new( move || #auto_complete(#reactive()) ); } } else { quote! {} } } ParsedChild::Select { .. } | ParsedChild::Textarea { .. } | ParsedChild::Checkbox { .. } => quote! {}, ParsedChild::Show { children, .. } => children.iter().map(auto_complete_signal).collect(), } } fn signal_reactive_base_names(name: &Ident) -> (Ident, Ident) { ( format_ident!("{name}_reactive_base_get"), format_ident!("{name}_reactive_base_set"), ) } fn reactive_signal(child: &ParsedChild) -> TokenStream2 { match child { ParsedChild::Input { name, reactive, rust_type, .. } => { if let Some(reactive) = reactive { let (signal_reactive_base_get, signal_reactive_base_set) = signal_reactive_base_names(name); quote! { let (#signal_reactive_base_get, #signal_reactive_base_set) = signal(None); #reactive = move || { { let output: Option