summaryrefslogtreecommitdiffstats
path: root/rocie-macros
diff options
context:
space:
mode:
Diffstat (limited to 'rocie-macros')
-rw-r--r--rocie-macros/src/form/generate.rs136
-rw-r--r--rocie-macros/src/form/mod.rs11
-rw-r--r--rocie-macros/src/form/parse.rs18
3 files changed, 146 insertions, 19 deletions
diff --git a/rocie-macros/src/form/generate.rs b/rocie-macros/src/form/generate.rs
index 5642e6a..89acea8 100644
--- a/rocie-macros/src/form/generate.rs
+++ b/rocie-macros/src/form/generate.rs
@@ -1,7 +1,7 @@
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
-use syn::{Ident, Type, parse_macro_input};
+use syn::{Ident, Path, Type, TypePath, parse_macro_input, punctuated::Punctuated};
use crate::form::{ParsedChild, ParsedInput};
@@ -33,7 +33,9 @@ pub fn form_impl(item: TokenStream) -> TokenStream {
let output = quote!({
use crate::components::{
input_placeholder::InputPlaceholder,
- select_placeholder::SelectPlaceholder
+ select_placeholder::SelectPlaceholder,
+ textarea_placeholder::TextareaPlaceholder,
+ checkbox_placeholder::CheckboxPlaceholder,
};
use leptos::{
@@ -48,7 +50,7 @@ pub fn form_impl(item: TokenStream) -> TokenStream {
Set,
Show,
},
- html::{Input, Select},
+ html::{Input, Select, Textarea},
web_sys::SubmitEvent
};
@@ -126,6 +128,8 @@ fn node_ref(child: &ParsedChild) -> TokenStream2 {
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"),
};
@@ -149,7 +153,9 @@ fn signal(child: &ParsedChild) -> TokenStream2 {
let (#signal_name_get, #signal_name_set) = signal(None);
}
}
- ParsedChild::Select { .. } => quote! {},
+ ParsedChild::Textarea { .. }
+ | ParsedChild::Checkbox { .. }
+ | ParsedChild::Select { .. } => quote! {},
ParsedChild::Show { children, .. } => children.iter().map(signal).collect(),
}
}
@@ -185,7 +191,9 @@ fn auto_complete_signal(child: &ParsedChild) -> TokenStream2 {
quote! {}
}
}
- ParsedChild::Select { .. } => quote! {},
+ ParsedChild::Select { .. }
+ | ParsedChild::Textarea { .. }
+ | ParsedChild::Checkbox { .. } => quote! {},
ParsedChild::Show { children, .. } => children.iter().map(auto_complete_signal).collect(),
}
}
@@ -238,14 +246,16 @@ fn reactive_signal(child: &ParsedChild) -> TokenStream2 {
quote! {}
}
}
- ParsedChild::Select { .. } => quote! {},
+ ParsedChild::Select { .. }
+ | ParsedChild::Textarea { .. }
+ | ParsedChild::Checkbox { .. } => quote! {},
ParsedChild::Show { children, .. } => children.iter().map(reactive_signal).collect(),
}
}
fn type_verification(child: &ParsedChild) -> TokenStream2 {
match child {
- ParsedChild::Input { .. } => {
+ ParsedChild::Input { .. } | ParsedChild::Textarea { .. } | ParsedChild::Checkbox { .. } => {
quote! {}
}
ParsedChild::Select { name, options, .. } => {
@@ -278,24 +288,65 @@ fn type_verification(child: &ParsedChild) -> TokenStream2 {
fn get_names(input: &ParsedChild) -> Vec<&Ident> {
match input {
- ParsedChild::Input { name, .. } => vec![name],
- ParsedChild::Select { name, .. } => vec![name],
+ ParsedChild::Input { name, .. }
+ | ParsedChild::Textarea { name, .. }
+ | ParsedChild::Checkbox { name, .. }
+ | ParsedChild::Select { name, .. } => vec![name],
ParsedChild::Show { children, .. } => children.iter().flat_map(get_names).collect(),
}
}
fn output_struct_definition(children: &[ParsedChild]) -> TokenStream2 {
- fn get_rust_types(input: &ParsedChild) -> Vec<&Type> {
+ macro_rules! mk_type {
+ ($name:ident) => {{
+ let segments = {
+ let mut p = Punctuated::new();
+ p.push(syn::PathSegment {
+ ident: format_ident!(stringify!($name)),
+ arguments: syn::PathArguments::None,
+ });
+ p
+ };
+
+ Type::Path(TypePath {
+ qself: None,
+ path: Path {
+ leading_colon: None,
+ segments,
+ },
+ })
+ }};
+ }
+
+ let string: Type = mk_type!(String);
+ let boolean: Type = mk_type!(bool);
+
+ fn get_rust_types<'a>(
+ input: &'a ParsedChild,
+ string: &'a Type,
+ bool: &'a Type,
+ ) -> Vec<&'a Type> {
match input {
- ParsedChild::Input { rust_type, .. } => vec![rust_type],
- ParsedChild::Select { rust_type, .. } => vec![rust_type],
- ParsedChild::Show { children, .. } => {
- children.iter().flat_map(get_rust_types).collect()
+ ParsedChild::Textarea { .. } => {
+ vec![string]
}
+ ParsedChild::Checkbox { .. } => {
+ vec![bool]
+ }
+ ParsedChild::Input { rust_type, .. } | ParsedChild::Select { rust_type, .. } => {
+ vec![rust_type]
+ }
+ ParsedChild::Show { children, .. } => children
+ .iter()
+ .flat_map(|i| get_rust_types(i, string, bool))
+ .collect(),
}
}
+
let names = children.iter().flat_map(get_names);
- let rust_types = children.iter().flat_map(get_rust_types);
+ let rust_types = children
+ .iter()
+ .flat_map(|i| get_rust_types(i, &string, &boolean));
quote! {
struct Inputs {
@@ -358,6 +409,42 @@ fn fetch_value(child: &ParsedChild) -> TokenStream2 {
};
}
}
+ ParsedChild::Textarea { name, .. } => {
+ let node_ref_name = node_ref_name(name);
+
+ quote! {
+ let #name: String = {
+ let output = #node_ref_name
+ .get()
+ // event handlers can only fire after the view
+ // is mounted to the DOM, so the `NodeRef` will be `Some`
+ .expect("<textarea> to exist")
+ .value();
+
+ let fin: String = output.to_owned();
+
+ fin
+ };
+ }
+ }
+ ParsedChild::Checkbox { name, .. } => {
+ let node_ref_name = node_ref_name(name);
+
+ quote! {
+ let #name: bool = {
+ let output = #node_ref_name
+ .get()
+ // event handlers can only fire after the view
+ // is mounted to the DOM, so the `NodeRef` will be `Some`
+ .expect("<checkbox> to exist")
+ .value();
+
+ let fin: bool = if output == "on" { true } else { false };
+
+ fin
+ };
+ }
+ }
ParsedChild::Select {
name, rust_type, ..
} => {
@@ -370,7 +457,7 @@ fn fetch_value(child: &ParsedChild) -> TokenStream2 {
.get()
// event handlers can only fire after the view
// is mounted to the DOM, so the `NodeRef` will be `Some`
- .expect("<input> to exist")
+ .expect("<select> to exist")
.value();
let fin: Result<#rust_type, leptos::error::Error> = output
@@ -384,6 +471,9 @@ fn fetch_value(child: &ParsedChild) -> TokenStream2 {
ok
} ,
Err(err) => {
+ // TODO: This can certainly happen (think of an empty string in a numeric input.)
+ // As such, we should have an error field per input, that we can populate with the
+ // error we received here. <2025-12-30>
unreachable!("Should be ruled out at compile time: {err}")
}
}
@@ -450,6 +540,20 @@ fn input_html(child: &ParsedChild) -> TokenStream2 {
</Show>
}
}
+ ParsedChild::Textarea { name, label, .. } => {
+ let node_ref_name = node_ref_name(name);
+
+ quote! {
+ <TextareaPlaceholder label=#label node_ref=#node_ref_name />
+ }
+ }
+ ParsedChild::Checkbox { name, label } => {
+ let node_ref_name = node_ref_name(name);
+
+ quote! {
+ <CheckboxPlaceholder label=#label node_ref=#node_ref_name />
+ }
+ }
ParsedChild::Select {
name,
label,
diff --git a/rocie-macros/src/form/mod.rs b/rocie-macros/src/form/mod.rs
index b165750..978b081 100644
--- a/rocie-macros/src/form/mod.rs
+++ b/rocie-macros/src/form/mod.rs
@@ -5,14 +5,12 @@ mod parse;
pub use generate::form_impl;
-#[derive(Debug)]
pub struct ParsedOnSubmit {
inputs: Vec<Ident>,
block: Expr,
pub(crate) should_use_move: bool,
}
-#[derive(Debug)]
pub enum ParsedChild {
Input {
name: Ident,
@@ -22,6 +20,10 @@ pub enum ParsedChild {
reactive: Option<Ident>,
auto_complete: Option<Ident>,
},
+ Checkbox {
+ name: Ident,
+ label: LitStr,
+ },
Select {
name: Ident,
label: LitStr,
@@ -32,9 +34,12 @@ pub enum ParsedChild {
when: Expr,
children: Vec<ParsedChild>,
},
+ Textarea {
+ name: Ident,
+ label: LitStr,
+ },
}
-#[derive(Debug)]
pub struct ParsedInput {
on_submit: ParsedOnSubmit,
children: Vec<ParsedChild>,
diff --git a/rocie-macros/src/form/parse.rs b/rocie-macros/src/form/parse.rs
index 59a82c1..2cf8799 100644
--- a/rocie-macros/src/form/parse.rs
+++ b/rocie-macros/src/form/parse.rs
@@ -86,6 +86,15 @@ impl Parse for ParsedChild {
auto_complete,
}
}
+ variant if variant == format_ident!("Textarea") => {
+ let name = parse_key_value!(input, name as Ident);
+ let label = parse_key_value!(input, label as LitStr);
+
+ input.parse::<Token![/]>()?;
+ input.parse::<Token![>]>()?;
+
+ ParsedChild::Textarea { name, 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);
@@ -130,6 +139,15 @@ impl Parse for ParsedChild {
ParsedChild::Show { when, children }
}
+ variant if variant == format_ident!("Checkbox") => {
+ let name = parse_key_value!(input, name as Ident);
+ let label = parse_key_value!(input, label as LitStr);
+
+ input.parse::<Token![/]>()?;
+ input.parse::<Token![>]>()?;
+
+ ParsedChild::Checkbox { name, label }
+ }
_ => panic!("Unknown form child variant: {variant}"),
};