From 1fc165f2a5a3b6d77da2cfea2aa05e1db1c73577 Mon Sep 17 00:00:00 2001 From: Benedikt Peetz Date: Sun, 5 Oct 2025 18:27:05 +0200 Subject: feat(form): Re-write the form macro as a proc macro This allows more possibilities. --- rocie-macros/src/form/parse.rs | 132 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 rocie-macros/src/form/parse.rs (limited to 'rocie-macros/src/form/parse.rs') diff --git a/rocie-macros/src/form/parse.rs b/rocie-macros/src/form/parse.rs new file mode 100644 index 0000000..ef2087b --- /dev/null +++ b/rocie-macros/src/form/parse.rs @@ -0,0 +1,132 @@ +use quote::format_ident; +use syn::{bracketed, parenthesized, parse::{Parse, ParseStream}, Expr, Ident, LitStr, Token, Type}; + +use crate::form::{ParsedChild, ParsedInput, ParsedOnSubmit, SelectOptions}; + +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 + }}; +} + +impl Parse for SelectOptions { + fn parse(input: ParseStream) -> syn::Result { + let content; + bracketed!(content in input); + let inner = content.parse_terminated( + |arg| { + let paren; + parenthesized!(paren in arg); + let lit = paren.parse::()?; + paren.parse::()?; + let expr = paren.parse::()?; + + Ok((lit, expr)) + }, + Token![,], + )?; + + Ok(Self(inner.into_iter().collect())) + } +} + +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); + + ParsedChild::Input { + name, + rust_type, + html_type, + label, + } + } + 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 SelectOptions); + + ParsedChild::Select { + name, + label, + options, + rust_type, + } + } + _ => panic!("Unkown form child variant: {variant}"), + }; + + input.parse::()?; + input.parse::]>()?; + + Ok(output) + } +} + +impl Parse for ParsedOnSubmit { + fn parse(input: ParseStream) -> syn::Result { + let mut inputs = Vec::new(); + + 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 }) + } +} -- cgit 1.4.1