use crate::core::config::TraitBridgeConfig;
use crate::core::ir::{ApiSurface, FieldDef, TypeDef, TypeRef};
use super::bridge_targets_language;
fn inactive_carriers<'a>(trait_bridges: &'a [TraitBridgeConfig], target_spellings: &[&str]) -> Vec<(&'a str, &'a str)> {
trait_bridges
.iter()
.filter(|bridge| !bridge_targets_language(bridge, target_spellings))
.filter_map(|bridge| {
let options_type = bridge.options_type.as_deref()?;
let field = bridge.resolved_options_field()?;
Some((options_type, field))
})
.collect()
}
fn direct_named_type(ty: &TypeRef) -> Option<&str> {
match ty {
TypeRef::Named(name) => Some(name),
TypeRef::Optional(inner) => direct_named_type(inner),
_ => None,
}
}
fn is_matching_carrier(candidate: &FieldDef, carrier: &FieldDef) -> bool {
candidate.name == carrier.name && candidate.ty == carrier.ty && candidate.type_rust_path == carrier.type_rust_path
}
fn carrier_owner_names(api: &ApiSurface, options_type: &str, field_name: &str) -> Vec<String> {
let Some(options) = api.types.iter().find(|typ| typ.name == options_type) else {
return Vec::new();
};
let Some(carrier) = options.fields.iter().find(|field| field.name == field_name) else {
return Vec::new();
};
let mut owners = vec![options.name.clone()];
for related_name in options
.methods
.iter()
.flat_map(|method| &method.params)
.filter_map(|param| direct_named_type(¶m.ty))
{
let Some(related) = api.types.iter().find(|typ| typ.name == related_name) else {
continue;
};
if related.fields.iter().any(|field| is_matching_carrier(field, carrier))
&& !owners.iter().any(|owner| owner == related_name)
{
owners.push(related_name.to_string());
}
}
owners
}
fn exclude_carrier(typ: &mut TypeDef, field_name: &str) -> bool {
let Some(field) = typ
.fields
.iter_mut()
.find(|field| field.name == field_name && !field.binding_excluded)
else {
return false;
};
field.binding_excluded = true;
field.binding_exclusion_reason = Some("trait bridge excluded for this language via exclude_languages".to_string());
true
}
pub fn prune_inactive_bridge_carriers(
api: &ApiSurface,
trait_bridges: &[TraitBridgeConfig],
target_spellings: &[&str],
) -> Option<ApiSurface> {
let carriers = inactive_carriers(trait_bridges, target_spellings);
if carriers.is_empty() {
return None;
}
let owners = carriers
.iter()
.flat_map(|(options_type, field_name)| {
carrier_owner_names(api, options_type, field_name)
.into_iter()
.map(|owner| (owner, *field_name))
})
.collect::<Vec<_>>();
let mut pruned = api.clone();
let mut marked = false;
for typ in &mut pruned.types {
for (owner, field_name) in &owners {
if typ.name != *owner {
continue;
}
marked |= exclude_carrier(typ, field_name);
}
}
marked.then_some(pruned)
}
pub fn language_surface(
api: &ApiSurface,
trait_bridges: &[TraitBridgeConfig],
language: crate::core::config::Language,
) -> Option<ApiSurface> {
prune_inactive_bridge_carriers(api, trait_bridges, language.bridge_spellings())
}
pub fn validated_language_surface<'a>(
pruned: &'a Option<ApiSurface>,
whole: crate::core::validation::ValidatedApiSurface<'a>,
) -> crate::core::validation::ValidatedApiSurface<'a> {
pruned.as_ref().map_or(
whole,
crate::core::validation::ValidatedApiSurface::from_subtractive_projection,
)
}