use crate::core::config::{ResolvedCrateConfig, TraitBridgeConfig};
use crate::core::ir::{ApiSurface, TypeRef};
pub(super) struct TraitBridgeFn {
pub(super) name: String,
pub(super) params: Vec<String>,
}
pub(super) fn collect_trait_bridge_fn_names(config: &ResolvedCrateConfig) -> ahash::AHashSet<String> {
let mut names = ahash::AHashSet::new();
for bridge_cfg in &config.trait_bridges {
if bridge_cfg.exclude_languages.iter().any(|l| l == "r" || l == "extendr") {
continue;
}
if let Some(name) = bridge_cfg.register_fn.as_deref() {
names.insert(name.to_string());
}
if let Some(name) = bridge_cfg.unregister_fn.as_deref() {
names.insert(name.to_string());
}
if let Some(name) = bridge_cfg.clear_fn.as_deref() {
names.insert(name.to_string());
}
}
names
}
pub(super) fn collect_trait_bridge_functions(config: &ResolvedCrateConfig) -> Vec<TraitBridgeFn> {
let mut out = Vec::new();
for bridge_cfg in &config.trait_bridges {
if bridge_cfg.exclude_languages.iter().any(|l| l == "r" || l == "extendr") {
continue;
}
if let Some(name) = bridge_cfg.register_fn.as_deref() {
out.push(TraitBridgeFn {
name: name.to_string(),
params: vec!["r_backend".to_string()],
});
}
if let Some(name) = bridge_cfg.unregister_fn.as_deref() {
out.push(TraitBridgeFn {
name: name.to_string(),
params: vec!["name".to_string()],
});
}
if let Some(name) = bridge_cfg.clear_fn.as_deref() {
out.push(TraitBridgeFn {
name: name.to_string(),
params: Vec::new(),
});
}
}
out
}
fn collect_bridge_handle_aliases(bridges: &[TraitBridgeConfig]) -> ahash::AHashSet<String> {
bridges.iter().filter_map(|bridge| bridge.type_alias.clone()).collect()
}
pub(super) fn collect_excluded_class_types(api: &ApiSurface, bridges: &[TraitBridgeConfig]) -> ahash::AHashSet<String> {
let opaque_types: ahash::AHashSet<String> = api
.types
.iter()
.filter(|t| t.is_opaque)
.map(|t| t.name.clone())
.collect();
let enum_names: ahash::AHashSet<String> = api.enums.iter().map(|e| e.name.clone()).collect();
let bridge_handle_aliases = collect_bridge_handle_aliases(bridges);
let arc_incompatible: ahash::AHashSet<String> = api
.types
.iter()
.filter(|t| t.is_opaque && bridge_handle_aliases.contains(&t.name))
.map(|t| t.name.clone())
.collect();
let is_struct_like =
|n: &str| -> bool { !opaque_types.contains(n) && !enum_names.contains(n) && !arc_incompatible.contains(n) };
let is_native_incompatible = |ty: &TypeRef| -> bool {
match ty {
TypeRef::Vec(inner) => match inner.as_ref() {
TypeRef::Named(n) if is_struct_like(n) => true,
TypeRef::Vec(_) => true, _ => false,
},
TypeRef::Optional(inner) => match inner.as_ref() {
TypeRef::Vec(inner2) => match inner2.as_ref() {
TypeRef::Named(n) if is_struct_like(n) => true,
TypeRef::Vec(_) => true, _ => false,
},
_ => false,
},
_ => false,
}
};
let mut excluded: ahash::AHashSet<String> = api
.types
.iter()
.filter(|t| t.is_trait)
.map(|t| t.name.clone())
.collect();
for t in &arc_incompatible {
excluded.insert(t.clone());
}
for t in &api.types {
if t.is_opaque || t.is_trait {
continue;
}
if t.fields.iter().any(|f| is_native_incompatible(&f.ty)) {
excluded.insert(t.name.clone());
}
}
excluded
}
pub(super) fn method_is_excluded_from_impl(
method: &crate::core::ir::MethodDef,
api: &ApiSurface,
bridges: &[TraitBridgeConfig],
) -> bool {
let opaque_types: ahash::AHashSet<String> = api
.types
.iter()
.filter(|t| t.is_opaque)
.map(|t| t.name.clone())
.collect();
let enum_names: ahash::AHashSet<String> = api.enums.iter().map(|e| e.name.clone()).collect();
let bridge_handle_aliases = collect_bridge_handle_aliases(bridges);
let arc_incompatible: ahash::AHashSet<String> = api
.types
.iter()
.filter(|t| t.is_opaque && bridge_handle_aliases.contains(&t.name))
.map(|t| t.name.clone())
.collect();
let references_arc_incompatible = |ty: &TypeRef| -> bool {
match ty {
TypeRef::Named(n) => arc_incompatible.contains(n),
TypeRef::Optional(inner) => matches!(inner.as_ref(), TypeRef::Named(n) if arc_incompatible.contains(n)),
_ => false,
}
};
let references_enum = |ty: &TypeRef| -> bool {
match ty {
TypeRef::Named(n) => enum_names.contains(n.as_str()),
TypeRef::Optional(inner) => matches!(inner.as_ref(), TypeRef::Named(n) if enum_names.contains(n.as_str())),
_ => false,
}
};
let param_is_owned_struct = |ty: &TypeRef| -> bool {
let is_non_opaque_struct =
|n: &str| !opaque_types.contains(n) && !enum_names.contains(n) && !arc_incompatible.contains(n);
match ty {
TypeRef::Named(n) => is_non_opaque_struct(n),
TypeRef::Optional(inner) => matches!(inner.as_ref(), TypeRef::Named(n) if is_non_opaque_struct(n)),
_ => false,
}
};
if references_arc_incompatible(&method.return_type)
|| method.params.iter().any(|p| references_arc_incompatible(&p.ty))
{
return true;
}
if references_enum(&method.return_type)
|| method
.params
.iter()
.any(|p| references_enum(&p.ty) || param_is_owned_struct(&p.ty))
{
return true;
}
let references_map = |ty: &TypeRef| -> bool {
match ty {
TypeRef::Map(_, _) => true,
TypeRef::Optional(inner) => matches!(inner.as_ref(), TypeRef::Map(_, _)),
_ => false,
}
};
if references_map(&method.return_type) || method.params.iter().any(|p| references_map(&p.ty)) {
return true;
}
if method.sanitized {
return true;
}
false
}