Skip to main content

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}