feat(derive): require form parameter for form handles
Emit handle-form facts from slhx-build and have #[slhx::handler] reject generated form handlers that do not accept slhx::Form<_>. Cover the boundary with compile-fail fixtures. req: form/004 req: form/006 req: test/003
This commit is contained in:
+25
-2
@@ -30,6 +30,16 @@ pub fn handler(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
)
|
||||
.into();
|
||||
}
|
||||
if handle_requires_form(&syms_path, &name) && !has_form_param(&function) {
|
||||
let message = format!(
|
||||
"slhx handler `{name}` handles a generated form and must accept slhx::Form<_>"
|
||||
);
|
||||
return quote!(
|
||||
#function
|
||||
compile_error!(#message);
|
||||
)
|
||||
.into();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,6 +135,17 @@ fn syms_contains_handle(path: &PathBuf, ident: &str) -> bool {
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_requires_form(path: &PathBuf, ident: &str) -> bool {
|
||||
let Ok(syms) = std::fs::read_to_string(path) else {
|
||||
return false;
|
||||
};
|
||||
syms.lines().any(|line| {
|
||||
let mut fields = line.split('\t');
|
||||
matches!(fields.next(), Some("handle_form"))
|
||||
&& fields.next().is_some_and(|handle_ident| handle_ident == ident)
|
||||
})
|
||||
}
|
||||
|
||||
fn compile_error(message: &str) -> TokenStream {
|
||||
format!("compile_error!({message:?});")
|
||||
.parse()
|
||||
@@ -133,7 +154,7 @@ fn compile_error(message: &str) -> TokenStream {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{has_form_param, has_non_unit_return, syms_contains_handle};
|
||||
use super::{handle_requires_form, has_form_param, has_non_unit_return, syms_contains_handle};
|
||||
use syn::parse_quote;
|
||||
|
||||
#[test]
|
||||
@@ -141,12 +162,14 @@ mod tests {
|
||||
let path = std::env::temp_dir().join("slhx-derive-syms-test.syms");
|
||||
std::fs::write(
|
||||
&path,
|
||||
"slhx-syms-v1\nslot\ttemplates/a.heml::count\tcount\t1\nhandle\ttemplates/a.heml::create\tcreate\t2\n",
|
||||
"slhx-syms-v1\nslot\ttemplates/a.heml::count\tcount\t1\nhandle\ttemplates/a.heml::create\tcreate\t2\nhandle_form\tcreate\tnew_todo\n",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert!(syms_contains_handle(&path, "create"));
|
||||
assert!(!syms_contains_handle(&path, "missing"));
|
||||
assert!(handle_requires_form(&path, "create"));
|
||||
assert!(!handle_requires_form(&path, "missing"));
|
||||
|
||||
let _ = std::fs::remove_file(path);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user