fix(derive): require generated handler symbols
Make #[slhx::handler] fail when slhx.syms is missing instead of silently skipping generated handle checks. Add compile-fail coverage with an actionable build.rs hint. req: build/003 req: test/003
This commit is contained in:
+48
-33
@@ -8,39 +8,54 @@ pub fn handler(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
|||||||
let function = parse_macro_input!(item as ItemFn);
|
let function = parse_macro_input!(item as ItemFn);
|
||||||
let name = function.sig.ident.to_string();
|
let name = function.sig.ident.to_string();
|
||||||
|
|
||||||
if let Some(syms_path) = syms_path() {
|
let Some(syms_path) = syms_path() else {
|
||||||
if syms_path.exists() {
|
let message = "#[slhx::handler] requires OUT_DIR; run inside a Cargo crate with slhx_build::app() in build.rs";
|
||||||
if !syms_contains_handle(&syms_path, &name) {
|
return quote!(
|
||||||
let message = format!(
|
#function
|
||||||
"unknown slhx handle `{name}`; add `data-slhx-handle=\"{name}\"` to a template or rename this handler"
|
compile_error!(#message);
|
||||||
);
|
)
|
||||||
return quote!(
|
.into();
|
||||||
#function
|
};
|
||||||
compile_error!(#message);
|
if !syms_path.exists() {
|
||||||
)
|
let message = format!(
|
||||||
.into();
|
"#[slhx::handler] could not find {}; add slhx_build::app().run()? to build.rs or check template generation",
|
||||||
}
|
syms_path.display()
|
||||||
if !has_form_param(&function) && !has_non_unit_return(&function) {
|
);
|
||||||
let message = format!(
|
return quote!(
|
||||||
"slhx handler `{name}` must accept a form/context parameter or return a value implementing IntoEffect"
|
#function
|
||||||
);
|
compile_error!(#message);
|
||||||
return quote!(
|
)
|
||||||
#function
|
.into();
|
||||||
compile_error!(#message);
|
}
|
||||||
)
|
if !syms_contains_handle(&syms_path, &name) {
|
||||||
.into();
|
let message = format!(
|
||||||
}
|
"unknown slhx handle `{name}`; add `data-slhx-handle=\"{name}\"` to a template or rename this handler"
|
||||||
if handle_requires_form(&syms_path, &name) && !has_form_param(&function) {
|
);
|
||||||
let message = format!(
|
return quote!(
|
||||||
"slhx handler `{name}` handles a generated form and must accept slhx::Form<_>"
|
#function
|
||||||
);
|
compile_error!(#message);
|
||||||
return quote!(
|
)
|
||||||
#function
|
.into();
|
||||||
compile_error!(#message);
|
}
|
||||||
)
|
if !has_form_param(&function) && !has_non_unit_return(&function) {
|
||||||
.into();
|
let message = format!(
|
||||||
}
|
"slhx handler `{name}` must accept a form/context parameter or return a value implementing IntoEffect"
|
||||||
}
|
);
|
||||||
|
return quote!(
|
||||||
|
#function
|
||||||
|
compile_error!(#message);
|
||||||
|
)
|
||||||
|
.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();
|
||||||
}
|
}
|
||||||
|
|
||||||
quote!(#function).into()
|
quote!(#function).into()
|
||||||
|
|||||||
@@ -60,6 +60,57 @@ fn known() {}
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn handler_macro_reports_missing_syms() {
|
||||||
|
// req: build/003 req: test/003
|
||||||
|
let fixture = Fixture::new("slhx-derive-handler-syms-fail");
|
||||||
|
fixture.write(
|
||||||
|
"Cargo.toml",
|
||||||
|
&format!(
|
||||||
|
r#"[package]
|
||||||
|
name = "slhx-derive-handler-syms-fail"
|
||||||
|
version = "0.0.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
slhx = {{ path = {:?} }}
|
||||||
|
"#,
|
||||||
|
repo_path("slhx")
|
||||||
|
),
|
||||||
|
);
|
||||||
|
fixture.write(
|
||||||
|
"build.rs",
|
||||||
|
r#"fn main() {
|
||||||
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
fixture.write(
|
||||||
|
"src/lib.rs",
|
||||||
|
r#"#[slhx::handler]
|
||||||
|
fn create() -> impl slhx::IntoEffect {
|
||||||
|
slhx::EffectBatch::default()
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
let output = check_fixture(&fixture);
|
||||||
|
|
||||||
|
assert!(!output.status.success(), "fixture unexpectedly compiled");
|
||||||
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
|
assert!(
|
||||||
|
stderr.contains("#[slhx::handler] could not find"),
|
||||||
|
"missing handler syms diagnostic in stderr:\n{stderr}"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
stderr.contains("add slhx_build::app().run()? to build.rs"),
|
||||||
|
"missing build.rs hint in stderr:\n{stderr}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn form_handle_requires_form_parameter() {
|
fn form_handle_requires_form_parameter() {
|
||||||
// req: form/004 req: form/006 req: test/003
|
// req: form/004 req: form/006 req: test/003
|
||||||
|
|||||||
Reference in New Issue
Block a user