diff options
| author | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2025-10-05 18:27:05 +0200 |
|---|---|---|
| committer | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2025-10-05 18:27:05 +0200 |
| commit | 1fc165f2a5a3b6d77da2cfea2aa05e1db1c73577 (patch) | |
| tree | 2ea10b7aa960ecfa932b1091a43f101c5668cea8 /rocie-macros/src/form/parse.rs | |
| parent | feat(form): Provide basic form framework (diff) | |
| download | web-client-1fc165f2a5a3b6d77da2cfea2aa05e1db1c73577.zip | |
feat(form): Re-write the form macro as a proc macro
This allows more possibilities.
Diffstat (limited to 'rocie-macros/src/form/parse.rs')
| -rw-r--r-- | rocie-macros/src/form/parse.rs | 132 |
1 files changed, 132 insertions, 0 deletions
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::<Ident>()?; + $input.parse::<Token![=]>()?; + 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::<Token![,]>()?; + } + + value + }}; +} + +impl Parse for SelectOptions { + fn parse(input: ParseStream) -> syn::Result<Self> { + let content; + bracketed!(content in input); + let inner = content.parse_terminated( + |arg| { + let paren; + parenthesized!(paren in arg); + let lit = paren.parse::<LitStr>()?; + paren.parse::<Token![,]>()?; + let expr = paren.parse::<Expr>()?; + + Ok((lit, expr)) + }, + Token![,], + )?; + + Ok(Self(inner.into_iter().collect())) + } +} + +impl Parse for ParsedInput { + fn parse(input: ParseStream) -> syn::Result<Self> { + let on_submit = input.parse::<syn::Ident>()?; + + if on_submit != format_ident!("on_submit") { + panic!("Did not find correct `on_submit`: {on_submit}") + } + + input.parse::<Token![=]>()?; + + let on_submit = input.parse::<ParsedOnSubmit>()?; + + let mut children = Vec::new(); + while !input.is_empty() { + children.push(input.parse::<ParsedChild>()?); + } + + Ok(Self { + on_submit, + children, + }) + } +} + +impl Parse for ParsedChild { + fn parse(input: ParseStream) -> syn::Result<Self> { + input.parse::<Token![<]>()?; + + let variant = input.parse::<Ident>()?; + + 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::<Token![/]>()?; + input.parse::<Token![>]>()?; + + Ok(output) + } +} + +impl Parse for ParsedOnSubmit { + fn parse(input: ParseStream) -> syn::Result<Self> { + let mut inputs = Vec::new(); + + input.parse::<Token![|]>()?; + while !input.peek(Token![|]) { + inputs.push(input.parse::<Ident>()?); + + if input.peek(Token![,]) { + input.parse::<Token![,]>()?; + } + } + input.parse::<Token![|]>()?; + + let block = input.parse::<Expr>()?; + + input.parse::<Token![;]>()?; + + Ok(Self { inputs, block }) + } +} |
