use super::Pyo3Backend;
use crate::core::backend::Backend;
use crate::core::config::{NewAlefConfig, ResolvedCrateConfig};
use crate::core::ir::{ApiSurface, EnumDef, EnumVariant, FunctionDef, ParamDef, TypeRef};
fn pyo3_config_with_feature(configured_feature: Option<&str>) -> ResolvedCrateConfig {
let features_line = configured_feature
.map(|f| format!("features = [\"{f}\"]\n"))
.unwrap_or_default();
let toml_src = format!(
"[workspace]\nlanguages = [\"python\"]\n[[crates]]\nname = \"test-lib\"\nsources = [\"src/lib.rs\"]\n\
[crates.python]\nmodule_name = \"test_lib\"\n{features_line}"
);
let cfg: NewAlefConfig = toml::from_str(&toml_src).unwrap();
cfg.resolve().unwrap().remove(0)
}
fn foreign_cfg_enum_api() -> ApiSurface {
ApiSurface {
crate_name: "test-lib".to_string(),
version: "0.1.0".to_string(),
enums: vec![EnumDef {
name: "RoutingStrategy".to_string(),
rust_path: "dep_crate::RoutingStrategy".to_string(),
variants: vec![
EnumVariant {
name: "Primary".to_string(),
..Default::default()
},
EnumVariant {
name: "Extra".to_string(),
cfg: Some(r#"feature = "extra-tier""#.to_string()),
..Default::default()
},
],
..Default::default()
}],
..Default::default()
}
}
fn foreign_cfg_enum_api_with_param_function() -> ApiSurface {
let mut api = foreign_cfg_enum_api();
api.functions.push(FunctionDef {
name: "set_routing_strategy".to_string(),
rust_path: "test_lib::set_routing_strategy".to_string(),
params: vec![ParamDef {
name: "strategy".to_string(),
ty: TypeRef::Named("RoutingStrategy".to_string()),
..Default::default()
}],
return_type: TypeRef::Unit,
..Default::default()
});
api
}
fn lib_rs_content(files: &[crate::core::backend::GeneratedFile]) -> &str {
&files
.iter()
.find(|f| f.path.to_string_lossy().ends_with("lib.rs"))
.expect("generate_bindings must emit lib.rs")
.content
}
fn core_to_binding_conversion(lib_rs: &str) -> &str {
let start = lib_rs
.find("impl From<dep_crate::RoutingStrategy> for RoutingStrategy {")
.expect("generated crate must convert the foreign enum from core to the binding type");
let end = lib_rs[start..]
.find("\n}")
.map(|i| start + i + 2)
.expect("conversion impl must close");
&lib_rs[start..end]
}
fn binding_to_core_conversion(lib_rs: &str) -> &str {
let start = lib_rs
.find("impl From<RoutingStrategy> for dep_crate::RoutingStrategy {")
.expect("generated crate must convert the binding enum back to the foreign core type");
let end = lib_rs[start..]
.find("\n}")
.map(|i| start + i + 2)
.expect("conversion impl must close");
&lib_rs[start..end]
}
#[test]
fn generate_bindings_omits_unreachable_catch_all_for_foreign_variant_proven_unreachable_end_to_end() {
let api = foreign_cfg_enum_api();
let config = pyo3_config_with_feature(None);
let files = Pyo3Backend.generate_bindings(&api, &config).unwrap();
let lib_rs = lib_rs_content(&files);
let conversion = core_to_binding_conversion(lib_rs);
assert!(
!conversion.contains("_ => Default::default(),"),
"a foreign cfg-gated variant proven unreachable by this binding's own configured feature \
set must not leave behind an unreachable catch-all (a cargo clippy -D warnings failure), \
got:\n{conversion}"
);
}
#[test]
fn generate_bindings_keeps_catch_all_for_foreign_variant_not_proven_unreachable_end_to_end() {
let api = foreign_cfg_enum_api();
let config = pyo3_config_with_feature(Some("extra-tier"));
let files = Pyo3Backend.generate_bindings(&api, &config).unwrap();
let lib_rs = lib_rs_content(&files);
let conversion = core_to_binding_conversion(lib_rs);
assert!(
conversion.contains("_ => Default::default(),"),
"a foreign cfg-gated variant that is NOT proven unreachable must keep the catch-all so the \
match stays exhaustive, got:\n{conversion}"
);
}
#[test]
fn generate_bindings_omits_binding_to_core_catch_all_for_foreign_variant_proven_unreachable_end_to_end() {
let api = foreign_cfg_enum_api_with_param_function();
let config = pyo3_config_with_feature(None);
let files = Pyo3Backend.generate_bindings(&api, &config).unwrap();
let lib_rs = lib_rs_content(&files);
let conversion = binding_to_core_conversion(lib_rs);
assert!(
!conversion.contains("_ => Default::default(),"),
"the PyO3 wrapper enum now drops a foreign variant proven unreachable, so the \
binding->core match is exhaustive without a catch-all -- keeping one is an unreachable \
pattern (a cargo clippy -D warnings failure), got:\n{conversion}"
);
assert!(
!conversion.contains("Extra"),
"the dropped foreign variant must not be named anywhere in the binding->core conversion, \
got:\n{conversion}"
);
}
#[test]
fn generate_bindings_keeps_binding_to_core_catch_all_for_foreign_variant_not_proven_unreachable_end_to_end() {
let api = foreign_cfg_enum_api_with_param_function();
let config = pyo3_config_with_feature(Some("extra-tier"));
let files = Pyo3Backend.generate_bindings(&api, &config).unwrap();
let lib_rs = lib_rs_content(&files);
let conversion = binding_to_core_conversion(lib_rs);
assert!(
conversion.contains("_ => Default::default(),"),
"a foreign cfg-gated variant that is NOT proven unreachable is still declared \
unconditionally, so the binding->core match must keep its catch-all, got:\n{conversion}"
);
}
fn foreign_cfg_enum_api_three_variants() -> ApiSurface {
ApiSurface {
crate_name: "test-lib".to_string(),
version: "0.1.0".to_string(),
enums: vec![EnumDef {
name: "RoutingStrategy".to_string(),
rust_path: "dep_crate::RoutingStrategy".to_string(),
variants: vec![
EnumVariant {
name: "Primary".to_string(),
..Default::default()
},
EnumVariant {
name: "Secondary".to_string(),
..Default::default()
},
EnumVariant {
name: "Extra".to_string(),
cfg: Some(r#"feature = "extra-tier""#.to_string()),
..Default::default()
},
],
..Default::default()
}],
..Default::default()
}
}
fn wrapper_enum_declaration(lib_rs: &str) -> &str {
let start = lib_rs
.find("pub enum RoutingStrategy {")
.expect("generated crate must declare the RoutingStrategy wrapper enum");
let end = lib_rs[start..]
.find("\n}")
.map(|i| start + i + 2)
.expect("enum declaration must close");
&lib_rs[start..end]
}
fn declared_variant_names(rendered: &str) -> std::collections::BTreeSet<String> {
rendered
.split(',')
.filter_map(|fragment| {
let fragment = fragment.rsplit(']').next()?;
let (name, rest) = fragment.trim().split_once(" = ")?;
let name = name.trim();
let rest: String = rest.chars().take_while(|c| !c.is_whitespace() && *c != '}').collect();
if name.is_empty() || rest.is_empty() || !rest.chars().all(|c| c.is_ascii_digit()) {
return None;
}
Some(name.to_string())
})
.collect()
}
fn names(values: &[&str]) -> std::collections::BTreeSet<String> {
values.iter().map(|s| s.to_string()).collect()
}
#[test]
fn generate_bindings_declares_exact_retained_variant_set_for_foreign_variant_proven_unreachable() {
let api = foreign_cfg_enum_api_three_variants();
let excluded_config = pyo3_config_with_feature(None);
let excluded_files = Pyo3Backend.generate_bindings(&api, &excluded_config).unwrap();
let excluded_decl = wrapper_enum_declaration(lib_rs_content(&excluded_files));
assert_eq!(
declared_variant_names(excluded_decl),
names(&["Primary", "Secondary"]),
"the declared set must be exactly the two retained variants, got:\n{excluded_decl}"
);
let active_config = pyo3_config_with_feature(Some("extra-tier"));
let active_files = Pyo3Backend.generate_bindings(&api, &active_config).unwrap();
let active_decl = wrapper_enum_declaration(lib_rs_content(&active_files));
assert_eq!(
declared_variant_names(active_decl),
names(&["Primary", "Secondary", "Extra"]),
"with \"extra-tier\" configured, the declared set must include the retained foreign \
variant, got:\n{active_decl}"
);
}
#[test]
fn generate_bindings_never_drops_host_owned_cfg_variant_from_declaration() {
let mut api = foreign_cfg_enum_api_three_variants();
api.enums[0].rust_path = "test_lib::RoutingStrategy".to_string();
let config = pyo3_config_with_feature(None);
let files = Pyo3Backend.generate_bindings(&api, &config).unwrap();
let decl = wrapper_enum_declaration(lib_rs_content(&files));
assert_eq!(
declared_variant_names(decl),
names(&["Primary", "Secondary", "Extra"]),
"a host-owned cfg-gated variant must stay declared even with no features configured, \
got:\n{decl}"
);
}