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}