use quote::format_ident; use syn::{ Expr, Ident, LitStr, Token, Type, parse::{Parse, ParseStream}, }; use crate::form::{ParsedChild, ParsedInput, ParsedOnSubmit}; macro_rules! parse_key_value { ($input:expr, $name:ident as $ty:ty) => {{ let key = $input.parse::()?; $input.parse::()?; let value = $input.parse::<$ty>()?; if key != format_ident!(stringify!($name)) { panic!("Expected key name to be {}, but found: {key}", stringify!($name)); } if $input.peek(Token![,]) { $input.parse::()?; } value }}; (@option $input:expr, $name:ident as $ty:ty) => {{ if $input.peek(Ident) { Some(parse_key_value!($input, $name as $ty)) } else { None } }}; } impl Parse for ParsedInput { fn parse(input: ParseStream) -> syn::Result { let on_submit = input.parse::()?; if on_submit != format_ident!("on_submit") { panic!("Did not find correct `on_submit`: {on_submit}") } input.parse::()?; let on_submit = input.parse::()?; let mut children = Vec::new(); while !input.is_empty() { children.push(input.parse::()?); } Ok(Self { on_submit, children, }) } } impl Parse for ParsedChild { fn parse(input: ParseStream) -> syn::Result { input.parse::()?; let variant = input.parse::()?; let output = match variant { variant if variant == format_ident!("Input") => { let name = parse_key_value!(input, name as Ident); let rust_type = parse_key_value!(input, rust_type as Type); let html_type = parse_key_value!(input, html_type as LitStr); let label = parse_key_value!(input, label as LitStr); let reactive = parse_key_value!(@option input, reactive as Ident); let auto_complete = parse_key_value!(@option input, auto_complete as Ident); if auto_complete.is_some() && reactive.is_none() { panic!("Cannot provide an autocomplet, without registering an reactive signal") } input.parse::()?; input.parse::]>()?; ParsedChild::Input { name, rust_type, html_type, label, reactive, auto_complete, } } variant if variant == format_ident!("Select") => { let name = parse_key_value!(input, name as Ident); let rust_type = parse_key_value!(input, rust_type as Type); let label = parse_key_value!(input, label as LitStr); let options = parse_key_value!(input, options as Expr); input.parse::()?; input.parse::]>()?; ParsedChild::Select { name, label, options, rust_type, } } variant if variant == format_ident!("Show") => { let when = parse_key_value!(input, when as Expr); input.parse::]>()?; let children = { let mut children = vec![]; while !(input.peek(Token![<]) && input.peek2(Token![/])) { children.push(input.parse::()?); } { input.parse::()?; input.parse::()?; let show_ident = input.parse::()?; if show_ident != format_ident!("Show") { panic!("Expected key name to be 'Show', but found: '{show_ident}'",); } input.parse::]>()?; children } }; ParsedChild::Show { when, children } } _ => panic!("Unknown form child variant: {variant}"), }; Ok(output) } } impl Parse for ParsedOnSubmit { fn parse(input: ParseStream) -> syn::Result { let mut inputs = Vec::new(); let should_use_move = if input.peek(Token![move]) { input.parse::()?; true } else { false }; input.parse::()?; while !input.peek(Token![|]) { inputs.push(input.parse::()?); if input.peek(Token![,]) { input.parse::()?; } } input.parse::()?; let block = input.parse::()?; input.parse::()?; Ok(Self { inputs, block, should_use_move, }) } }