Skip to main content

ifc_spatial/relation/
link.rs

1//! Reading the two ends of an objectified relationship.
2
3use ifc_model::{EntityId, Model};
4
5use super::slots::RelSlots;
6
7/// Which objectified relationship a link came from.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum RelationshipKind {
10    /// `IfcRelAggregates` -- decomposition of a spatial structure or element.
11    Aggregates,
12    /// `IfcRelContainedInSpatialStructure` -- elements placed in a container.
13    ContainedIn,
14    /// `IfcRelNests` -- ordered decomposition.
15    Nests,
16    /// `IfcRelCoversBldgElements` -- finishes applied to an element.
17    CoversElements,
18    /// `IfcRelCoversSpaces` -- finishes bounding a space.
19    CoversSpaces,
20    /// `IfcRelConnectsElements` and its subtypes -- element adjacency.
21    ConnectsElements,
22    /// `IfcRelInterferesElements` -- elements sharing space (a clash).
23    InterferesElements,
24    /// `IfcRelAssignsToActor` -- responsibility for an object.
25    AssignsToActor,
26    /// `IfcRelAssignsToProcess` -- a task consuming or producing it.
27    AssignsToProcess,
28    /// `IfcRelAssignsToProduct` -- assignment to a product.
29    AssignsToProduct,
30    /// `IfcRelAssignsToGroupByFactor` -- group membership with a ratio.
31    AssignsToGroupByFactor,
32    /// `IfcRelDeclares` -- what a context declares.
33    Declares,
34    /// `IfcRelDefinesByObject` -- occurrence defined by an occurrence.
35    DefinesByObject,
36    /// `IfcRelFlowControlElements` -- controls governing a flow element.
37    FlowControlElements,
38    /// `IfcRelServicesBuildings` -- structures a system serves.
39    ServicesBuildings,
40    /// `IfcRelConnectsWithEccentricity` -- offset structural connection.
41    ConnectsWithEccentricity,
42}
43
44/// One relationship instance, resolved to its ends.
45#[derive(Debug, Clone, PartialEq, Eq)]
46pub struct Relationship {
47    /// The relationship entity itself.
48    pub id: EntityId,
49    /// Which relationship type this is.
50    pub kind: RelationshipKind,
51    /// The parent/owner end.
52    pub relating: Option<EntityId>,
53    /// The child/member ends, in file order.
54    pub related: Vec<EntityId>,
55}
56
57/// Collect the references held in one attribute slot.
58///
59/// The relating end is normally a single reference and the related end a list,
60/// but a malformed file can hold either shape in either slot, so both are read
61/// the same way and interpreted by the caller.
62pub(crate) fn refs_in_slot(model: &Model, id: EntityId, slot: usize) -> Vec<EntityId> {
63    let Some(entity) = model.get(id) else {
64        return Vec::new();
65    };
66    let Some(attribute) = entity.attribute(slot) else {
67        return Vec::new();
68    };
69    let mut out = Vec::new();
70    attribute.for_each_ref(&mut |target| out.push(target));
71    out
72}
73
74/// Read one relationship entity's ends, or `None` if it is not of this type.
75pub(crate) fn read(
76    model: &Model,
77    id: EntityId,
78    slots: RelSlots,
79    kind: RelationshipKind,
80) -> Option<Relationship> {
81    let entity = model.get(id)?;
82    if !entity.is_type(slots.type_name) {
83        return None;
84    }
85    // A relating end naming several entities is malformed; take the first and
86    // let the caller's validation report the rest rather than guessing.
87    let relating = refs_in_slot(model, id, slots.relating).into_iter().next();
88    Some(Relationship {
89        id,
90        kind,
91        relating,
92        related: refs_in_slot(model, id, slots.related),
93    })
94}