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