ifc_spatial/relation/index.rs
1//! Reusable inverse lookups for objectified spatial relationships.
2
3use ifc_model::{EntityId, Model, ReverseIndex};
4
5use super::link;
6use super::slots;
7use super::{Relationship, RelationshipKind};
8
9/// Reverse-reference index paired with the model snapshot it indexes.
10///
11/// Build this once when querying many entities. It preserves the tolerant
12/// decoding of [`super::naming`] while avoiding a relationship rescan per
13/// target. Mutating the borrowed model requires dropping this index first, so
14/// its derived references cannot silently become stale.
15#[derive(Debug)]
16pub struct RelationshipIndex<'m> {
17 model: &'m Model,
18 reverse: ReverseIndex,
19}
20
21impl<'m> RelationshipIndex<'m> {
22 /// Build one reverse index over the model snapshot.
23 #[must_use]
24 pub fn build(model: &'m Model) -> Self {
25 Self {
26 model,
27 reverse: ReverseIndex::build(model),
28 }
29 }
30
31 /// Relationships whose related/member end names `target`.
32 ///
33 /// Results use the same deterministic kind and entity ordering as
34 /// [`super::all`]. Malformed or unrelated records that merely reference the
35 /// target from the same absolute slot are ignored.
36 #[must_use]
37 pub fn naming(&self, target: EntityId) -> Vec<Relationship> {
38 let mut out = Vec::new();
39 for (rel_slots, kind) in [
40 (slots::AGGREGATES, RelationshipKind::Aggregates),
41 (slots::CONTAINED_IN, RelationshipKind::ContainedIn),
42 (slots::NESTS, RelationshipKind::Nests),
43 ] {
44 for id in self.reverse.referrers_in_slot(target, rel_slots.related) {
45 if let Some(relationship) = link::read(self.model, id, rel_slots, kind) {
46 out.push(relationship);
47 }
48 }
49 }
50 out
51 }
52}