use super::super::ExtendrBackend;
use super::make_config;
use crate::core::backend::Backend;
use crate::core::ir::*;
fn gated_variant(name: &str, cfg: Option<&str>) -> EnumVariant {
EnumVariant {
name: name.to_string(),
cfg: cfg.map(str::to_string),
..Default::default()
}
}
fn returning_function(name: &str, enum_name: &str) -> FunctionDef {
FunctionDef {
name: name.to_string(),
rust_path: format!("test_lib::{name}"),
return_type: TypeRef::Named(enum_name.to_string()),
..Default::default()
}
}
fn generate_r(api: &ApiSurface) -> String {
ExtendrBackend
.generate_bindings(api, &make_config())
.expect("extendr generation")
.iter()
.map(|f| format!("// ==== {} ====\n{}", f.path.display(), f.content))
.collect::<Vec<_>>()
.join("\n")
}
#[test]
fn host_owned_cfg_gated_variant_keeps_arm_under_matching_cfg_guard() {
let api = ApiSurface {
enums: vec![EnumDef {
name: "Status".to_string(),
rust_path: "test_lib::Status".to_string(),
variants: vec![
gated_variant("Active", None),
gated_variant("Beta", Some(r#"feature = "beta""#)),
],
..Default::default()
}],
functions: vec![returning_function("get_status", "Status")],
..Default::default()
};
let out = generate_r(&api);
assert!(
out.contains("Status::Active => Self::Active,"),
"ungated variant's binding->core arm missing, fixture no longer exercises conversion:\n{out}"
);
assert!(
out.contains("test_lib::Status::Active => Self::Active,"),
"ungated variant's core->binding arm missing, fixture no longer exercises conversion:\n{out}"
);
assert!(
out.contains("#[cfg(feature = \"beta\")]\n Status::Beta => Self::Beta,"),
"host-owned cfg-gated variant must keep its binding->core arm under a matching #[cfg(...)] guard:\n{out}"
);
assert!(
out.contains("#[cfg(feature = \"beta\")]\n test_lib::Status::Beta => Self::Beta,"),
"host-owned cfg-gated variant must keep its core->binding arm under a matching #[cfg(...)] guard:\n{out}"
);
}
#[test]
fn foreign_owned_cfg_gated_variant_drops_arm_entirely() {
let api = ApiSurface {
enums: vec![EnumDef {
name: "External".to_string(),
rust_path: "foreign_crate::External".to_string(),
variants: vec![
gated_variant("Foo", None),
gated_variant("Bar", Some(r#"feature = "extra""#)),
],
..Default::default()
}],
functions: vec![returning_function("get_external", "External")],
..Default::default()
};
let out = generate_r(&api);
assert!(
out.contains("External::Foo => Self::Foo,"),
"ungated variant's binding->core arm missing, fixture no longer exercises conversion:\n{out}"
);
assert!(
out.contains("foreign_crate::External::Foo => Self::Foo,"),
"ungated variant's core->binding arm missing, fixture no longer exercises conversion:\n{out}"
);
assert!(
!out.contains("External::Bar"),
"a foreign crate's cfg-gated variant must not be named anywhere in the conversion output:\n{out}"
);
assert!(
!out.contains(r#"#[cfg(feature = "extra")]"#),
"a foreign crate's cfg gate must never be forwarded into this generated crate:\n{out}"
);
}
#[test]
fn ungated_enum_emits_no_cfg_guard_and_no_catch_all() {
let api = ApiSurface {
enums: vec![EnumDef {
name: "Plain".to_string(),
rust_path: "test_lib::Plain".to_string(),
variants: vec![gated_variant("On", None), gated_variant("Off", None)],
..Default::default()
}],
functions: vec![returning_function("get_plain", "Plain")],
..Default::default()
};
let out = generate_r(&api);
assert!(
out.contains("Plain::On => Self::On,") && out.contains("Plain::Off => Self::Off,"),
"both ungated variants' binding->core arms must be present:\n{out}"
);
assert!(
!out.contains("#[cfg("),
"an ungated enum must not emit any #[cfg(...)] guard:\n{out}"
);
assert!(
!out.contains("_ => Self::default()"),
"an ungated enum with no data/excluded variants must not emit a catch-all fallback arm:\n{out}"
);
}