use crate::graph::types::FfiAbi;
pub(crate) struct AbiSpec {
pub abi: FfiAbi,
pub consumers: &'static [&'static str],
pub rust_attr_markers: &'static [&'static str],
pub rust_name_override_markers: &'static [&'static str],
pub name_prefix: Option<&'static str>,
}
pub(crate) const SPECS: &[AbiSpec] = &[
super::c::SPEC,
super::python::SPEC,
super::wasm::SPEC,
super::node_api::SPEC,
super::jni::SPEC,
];
pub(crate) fn consumers(abi: FfiAbi) -> &'static [&'static str] {
SPECS
.iter()
.find(|s| s.abi == abi)
.map_or(&[], |s| s.consumers)
}
fn reclassify_by_name(base: FfiAbi, name: &str) -> FfiAbi {
c_name_export_abi(name).unwrap_or(base)
}
pub(crate) fn c_name_export_abi(name: &str) -> Option<FfiAbi> {
SPECS
.iter()
.find(|s| s.name_prefix.is_some_and(|p| name.starts_with(p)))
.map(|s| s.abi)
}
pub(crate) fn rust_exports(attr_texts: &[&str], fn_name: &str) -> Vec<(FfiAbi, String)> {
let mut out = Vec::new();
for spec in SPECS {
if spec.rust_attr_markers.is_empty() {
continue;
}
let enabled = attr_texts
.iter()
.any(|t| spec.rust_attr_markers.iter().any(|m| t.contains(m)));
if !enabled {
continue;
}
let name = attr_texts
.iter()
.rev()
.filter(|t| {
spec.rust_name_override_markers
.iter()
.any(|m| t.contains(m))
})
.find_map(|t| first_quoted(t))
.map(str::to_owned)
.unwrap_or_else(|| fn_name.to_owned());
out.push((reclassify_by_name(spec.abi, &name), name));
}
out
}
fn first_quoted(s: &str) -> Option<&str> {
let start = s.find('"')? + 1;
let rest = &s[start..];
let end = rest.find('"')?;
Some(&rest[..end])
}