fix(forms): support reserved control identifiers

Normalize raw Rust identifiers when matching generated form contracts so reserved HTML control names remain usable.

req: form/004
This commit is contained in:
slhx agent
2026-07-16 10:00:43 +02:00
parent a04773d016
commit e7211df4c5
3 changed files with 74 additions and 8 deletions
+8 -2
View File
@@ -422,7 +422,7 @@ fn form_contract_errors(
field
.ident
.as_ref()
.map(|ident| (ident.to_string(), &field.ty))
.map(|ident| (form_field_name(ident), &field.ty))
})
.collect::<Vec<_>>();
let mut errors = Vec::new();
@@ -469,6 +469,10 @@ fn form_parser_types(form_struct: &ItemStruct) -> Vec<Type> {
.collect()
}
fn form_field_name(ident: &syn::Ident) -> String {
ident.to_string().trim_start_matches("r#").to_owned()
}
fn form_decode_fields(
syms_path: &PathBuf,
form_name: &str,
@@ -486,7 +490,9 @@ fn form_decode_fields(
form_fields(syms_path, form_name)
.into_iter()
.filter_map(|field| {
let (ident, ty) = actual.iter().find(|(ident, _)| ident == &&field.ident)?;
let (ident, ty) = actual
.iter()
.find(|(ident, _)| form_field_name(ident) == field.ident)?;
let control_name = field.name;
let parser = parser_type(ty);
Some(if field.multiple {
+51
View File
@@ -611,6 +611,57 @@ struct Profile {
);
}
#[test]
fn form_struct_accepts_raw_identifier_for_reserved_control_name() {
// req: form/004 test
let fixture = Fixture::new("hemx-derive-form-raw-identifier-pass");
fixture.write(
"Cargo.toml",
&format!(
r#"[package]
name = "hemx-derive-form-raw-identifier-pass"
version = "0.0.0"
edition = "2021"
[lib]
path = "src/lib.rs"
[dependencies]
hemx = {{ path = {:?} }}
"#,
repo_path("hemx")
),
);
fixture.write(
"build.rs",
r#"fn main() {
let out = std::path::PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
std::fs::write(
out.join("hemx.syms"),
"hemx-syms-v1\nform\ttemplates/app.heml::filter\tfilter\t1\nform_field\tfilter\ttype\ttrue\tfalse\n",
)
.unwrap();
}
"#,
);
fixture.write(
"src/lib.rs",
r#"#[hemx::form("filter")]
struct Filter {
r#type: String,
}
"#,
);
let output = check_fixture(&fixture);
assert!(
output.status.success(),
"fixture failed to compile:\n{}",
String::from_utf8_lossy(&output.stderr)
);
}
#[test]
fn form_handle_accepts_checked_form_model() {
// req: form/001 req: form/004 req: form/006