summary refs log tree commit diff stats
path: root/rocie-macros/src/form/parse.rs
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-10-05 18:27:05 +0200
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-10-05 18:27:05 +0200
commit1fc165f2a5a3b6d77da2cfea2aa05e1db1c73577 (patch)
tree2ea10b7aa960ecfa932b1091a43f101c5668cea8 /rocie-macros/src/form/parse.rs
parentfeat(form): Provide basic form framework (diff)
downloadweb-client-1fc165f2a5a3b6d77da2cfea2aa05e1db1c73577.zip
feat(form): Re-write the form macro as a proc macro
This allows more possibilities.
Diffstat (limited to '')
-rw-r--r--rocie-macros/src/form/parse.rs132
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 })
+    }
+}