use ahash::AHashMap;
use crate::value::EntityId;
use crate::Model;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Referrer {
pub from: EntityId,
pub slot: usize,
}
#[derive(Debug, Clone, Default)]
pub struct ReverseIndex {
incoming: AHashMap<EntityId, Vec<Referrer>>,
}
impl ReverseIndex {
#[must_use]
pub fn build(model: &Model) -> Self {
let mut incoming: AHashMap<EntityId, Vec<Referrer>> = AHashMap::new();
for from in model.ids() {
let Some(entity) = model.get(from) else {
continue;
};
for (slot, attribute) in entity.attributes.iter().enumerate() {
attribute.for_each_ref(&mut |target| {
incoming
.entry(target)
.or_default()
.push(Referrer { from, slot });
});
}
}
for referrers in incoming.values_mut() {
referrers.sort_unstable();
referrers.dedup();
}
Self { incoming }
}
#[must_use]
pub fn referrers(&self, target: EntityId) -> &[Referrer] {
self.incoming.get(&target).map_or(&[], Vec::as_slice)
}
pub fn referrers_in_slot(
&self,
target: EntityId,
slot: usize,
) -> impl Iterator<Item = EntityId> + '_ {
self.referrers(target)
.iter()
.filter(move |r| r.slot == slot)
.map(|r| r.from)
}
#[must_use]
pub fn is_referenced(&self, target: EntityId) -> bool {
!self.referrers(target).is_empty()
}
#[must_use]
pub fn len(&self) -> usize {
self.incoming.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.incoming.is_empty()
}
}