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}