Skip to main content

ifc_resource/query/
allocation.rs

1//! `IfcRelAssignsToResource` projections and inverse lookup.
2
3use ifc_model::EntityId;
4
5use crate::error::{ResourceError, ResourceResult};
6use crate::view::{validate_object_assignment, Record, ResourceView};
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9/// Decoded `IfcRelAssignsToResource` assignment: a resource and the objects
10/// assigned to it.
11pub struct ResourceAllocation {
12    relation: EntityId,
13    resource: EntityId,
14    related_objects: Vec<EntityId>,
15    related_objects_type: Option<String>,
16}
17
18impl ResourceAllocation {
19    /// The entity id of the `IfcRelAssignsToResource` relation itself.
20    #[must_use]
21    pub fn relation_id(&self) -> EntityId {
22        self.relation
23    }
24
25    /// The `RelatingResource` attribute: the assigned resource or resource
26    /// type.
27    #[must_use]
28    pub fn resource_id(&self) -> EntityId {
29        self.resource
30    }
31
32    /// The `RelatedObjects` attribute: objects the resource is assigned to.
33    #[must_use]
34    pub fn related_objects(&self) -> &[EntityId] {
35        &self.related_objects
36    }
37
38    /// The `RelatedObjectsType` enumeration, when authored.
39    #[must_use]
40    pub fn related_objects_type(&self) -> Option<&str> {
41        self.related_objects_type.as_deref()
42    }
43}
44
45impl<'m, 's> ResourceView<'m, 's> {
46    /// Projects an `IfcRelAssignsToResource` relation by entity id.
47    pub fn allocation(&self, id: EntityId) -> ResourceResult<ResourceAllocation> {
48        let record = self.record(id, "IfcRelAssignsToResource")?;
49        decode_allocation(record)
50    }
51
52    /// Every `IfcRelAssignsToResource` relation naming this resource as
53    /// `RelatingResource`, in ancestor order.
54    pub fn allocations_for(&self, resource: EntityId) -> ResourceResult<Vec<ResourceAllocation>> {
55        self.resource(resource)?;
56        let mut result = Vec::new();
57        for relation in self.ids_of_ancestor("IfcRelAssignsToResource") {
58            let allocation = self.allocation(relation)?;
59            if allocation.resource == resource {
60                result.push(allocation);
61            }
62        }
63        Ok(result)
64    }
65}
66
67fn decode_allocation(record: Record<'_, '_>) -> ResourceResult<ResourceAllocation> {
68    let resource = record.required_ref_select(
69        "RelatingResource",
70        "IfcResourceSelect",
71        &["IfcResource", "IfcTypeResource"],
72    )?;
73    let related_objects = record.refs("RelatedObjects", "IfcObjectDefinition", 1, false, true)?;
74    let related_objects_type = record.optional_enum("RelatedObjectsType")?;
75    validate_object_assignment(
76        record.model,
77        record.schema,
78        Some(record.id),
79        related_objects_type,
80        &related_objects,
81    )?;
82    if related_objects.contains(&resource) {
83        return Err(ResourceError::SemanticViolation {
84            entity: Some(record.id),
85            rule: "IfcRelAssignsToResource must not assign its resource to itself",
86        });
87    }
88    Ok(ResourceAllocation {
89        relation: record.id,
90        resource,
91        related_objects,
92        related_objects_type: related_objects_type.map(str::to_owned),
93    })
94}