use std::collections::HashSet;
use prebindgen_flat::flat::Flat;
use crate::{prebindgen::NamePredicate, registry::TypeKey};
#[derive(Default)]
pub struct Claimed {
pub functions: HashSet<syn::Ident>,
pub types: HashSet<TypeKey>,
pub consts: Option<HashSet<syn::Ident>>,
pub ignored_functions: HashSet<syn::Ident>,
pub ignored_types: HashSet<TypeKey>,
pub ignored_consts: HashSet<syn::Ident>,
pub ignored_name_predicates: Vec<NamePredicate>,
}
impl Claimed {
fn predicate_ignored(&self, name: &str) -> bool {
!self.ignored_name_predicates.is_empty()
&& self.ignored_name_predicates.iter().any(|p| p(name))
}
}
pub fn warn_unclaimed(flat: &Flat, claimed: &Claimed) {
for line in unclaimed_report(flat, claimed) {
println!("cargo:warning={line}");
}
}
pub(crate) fn unclaimed_report(flat: &Flat, claimed: &Claimed) -> Vec<String> {
let mut out = Vec::new();
for ident in sorted(claimed.ignored_functions.iter().map(|i| i.to_string())) {
if flat.function(&ident_of(&ident)).is_none() {
out.push(format!(
"prebindgen: ignored function `{ident}` not found among #[prebindgen] items"
));
}
}
for key in sorted(claimed.ignored_types.iter().map(|k| k.as_str().to_owned())) {
let named = TypeKey::parse(&key)
.ok()
.and_then(|k| k.ident())
.is_some_and(|ident| flat.declared_type(&ident).is_some());
if !named {
out.push(format!(
"prebindgen: ignored type `{key}` not found among #[prebindgen] items"
));
}
}
if claimed.consts.is_some() {
for ident in sorted(claimed.ignored_consts.iter().map(|i| i.to_string())) {
if flat.constant(&ident_of(&ident)).is_none() {
out.push(format!(
"prebindgen: ignored const `{ident}` not found among #[prebindgen] items"
));
}
}
}
for name in sorted(
flat.functions()
.map(|f| &f.name)
.filter(|k| !claimed.functions.contains(*k) && !claimed.ignored_functions.contains(*k))
.map(|k| k.to_string())
.filter(|n| !claimed.predicate_ignored(n)),
) {
out.push(format!(
"prebindgen: skipping undeclared #[prebindgen] fn `{name}`"
));
}
for name in sorted(
struct_enum_idents(flat)
.filter(|i| {
let key = TypeKey::from_ident(i);
!claimed.types.contains(&key) && !claimed.ignored_types.contains(&key)
})
.map(|i| i.to_string())
.filter(|n| !claimed.predicate_ignored(n)),
) {
out.push(format!(
"prebindgen: skipping undeclared #[prebindgen] struct/enum `{name}`"
));
}
if let Some(declared) = &claimed.consts {
for name in sorted(
flat.constants()
.map(|c| &c.name)
.filter(|k| !declared.contains(*k) && !claimed.ignored_consts.contains(*k))
.map(|k| k.to_string())
.filter(|n| !claimed.predicate_ignored(n)),
) {
out.push(format!(
"prebindgen: skipping undeclared #[prebindgen] const `{name}`"
));
}
}
out
}
fn struct_enum_idents(flat: &Flat) -> impl Iterator<Item = &syn::Ident> {
use prebindgen_flat::flat::Type;
flat.types().filter_map(|t| match t {
Type::Struct(_) | Type::Variant(_) | Type::Enum(_) => Some(t.name()),
Type::Extern(_) => None,
})
}
fn sorted(it: impl Iterator<Item = String>) -> Vec<String> {
let mut v: Vec<String> = it.collect();
v.sort();
v
}
fn ident_of(name: &str) -> syn::Ident {
syn::Ident::new(name, proc_macro2::Span::call_site())
}
#[cfg(test)]
mod tests;