Skip to main content

ifc_resource/resource/
base.rs

1//! IFC4 construction-resource occurrence projections.
2
3use ifc_model::EntityId;
4
5use crate::error::{ResourceError, ResourceResult};
6use crate::usage::ResourceTime;
7use crate::view::Record;
8
9/// Concrete `IfcConstructionResource` specialization, one of the six IFC4
10/// resource-occurrence entities this crate projects.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12#[non_exhaustive]
13pub enum ResourceKind {
14    /// `IfcLaborResource`: a person or crew role.
15    Labor,
16    /// `IfcConstructionEquipmentResource`: machinery or tooling.
17    Equipment,
18    /// `IfcCrewResource`: a composed group of resources.
19    Crew,
20    /// `IfcConstructionMaterialResource`: a consumed material.
21    Material,
22    /// `IfcConstructionProductResource`: a resource realized as a product.
23    Product,
24    /// `IfcSubContractResource`: work delegated to a subcontractor.
25    Subcontract,
26}
27
28impl ResourceKind {
29    fn from_type(type_name: &str) -> Option<Self> {
30        if type_name.eq_ignore_ascii_case("IfcLaborResource") {
31            Some(Self::Labor)
32        } else if type_name.eq_ignore_ascii_case("IfcConstructionEquipmentResource") {
33            Some(Self::Equipment)
34        } else if type_name.eq_ignore_ascii_case("IfcCrewResource") {
35            Some(Self::Crew)
36        } else if type_name.eq_ignore_ascii_case("IfcConstructionMaterialResource") {
37            Some(Self::Material)
38        } else if type_name.eq_ignore_ascii_case("IfcConstructionProductResource") {
39            Some(Self::Product)
40        } else if type_name.eq_ignore_ascii_case("IfcSubContractResource") {
41            Some(Self::Subcontract)
42        } else {
43            None
44        }
45    }
46}
47
48#[derive(Debug, Clone, Copy)]
49/// Borrowed projection of a concrete `IfcConstructionResource` occurrence.
50pub struct ConstructionResource<'m, 's> {
51    record: Record<'m, 's>,
52    kind: ResourceKind,
53}
54
55impl<'m, 's> ConstructionResource<'m, 's> {
56    pub(crate) fn from_record(record: Record<'m, 's>) -> ResourceResult<Self> {
57        let kind = ResourceKind::from_type(&record.entity.type_name).ok_or_else(|| {
58            ResourceError::WrongType {
59                id: record.id,
60                expected: "concrete IfcConstructionResource occurrence",
61                actual: record.entity.type_name.to_string(),
62            }
63        })?;
64        Ok(Self { record, kind })
65    }
66
67    /// The entity id of the projected occurrence.
68    #[must_use]
69    pub fn id(&self) -> EntityId {
70        self.record.id
71    }
72
73    /// Which concrete `IfcConstructionResource` subtype this occurrence is.
74    #[must_use]
75    pub fn kind(&self) -> ResourceKind {
76        self.kind
77    }
78
79    /// The `Name` attribute, when authored.
80    pub fn name(&self) -> ResourceResult<Option<&'m str>> {
81        self.record.optional_text("Name")
82    }
83
84    /// The `Identification` attribute, when authored.
85    pub fn identification(&self) -> ResourceResult<Option<&'m str>> {
86        self.record.optional_text("Identification")
87    }
88
89    /// The `LongDescription` attribute, when authored.
90    pub fn long_description(&self) -> ResourceResult<Option<&'m str>> {
91        self.record.optional_text("LongDescription")
92    }
93
94    /// The `PredefinedType` enumeration, when authored.
95    ///
96    /// A `USERDEFINED` value requires `ObjectType` to be set; that
97    /// constraint is enforced here rather than left to the caller.
98    pub fn predefined_type(&self) -> ResourceResult<Option<&'m str>> {
99        let value = self.record.optional_enum("PredefinedType")?;
100        self.record.require_object_type_if(
101            value.is_some_and(|value| value.eq_ignore_ascii_case("USERDEFINED")),
102            "USERDEFINED resource PredefinedType requires ObjectType",
103        )?;
104        Ok(value)
105    }
106
107    /// The `Usage` attribute: authored `IfcResourceTime`, when present.
108    pub fn usage(&self) -> ResourceResult<Option<ResourceTime<'m, 's>>> {
109        self.record
110            .optional_ref("Usage", "IfcResourceTime")?
111            .map(|id| Record::new(self.record.model, self.record.schema, id, "IfcResourceTime"))
112            .transpose()
113            .map(|record| record.map(ResourceTime::from_record))
114    }
115
116    /// The `BaseCosts` attribute: applied-value references, when authored.
117    pub fn base_costs(&self) -> ResourceResult<Vec<EntityId>> {
118        self.record
119            .refs("BaseCosts", "IfcAppliedValue", 1, true, false)
120    }
121
122    /// The `BaseQuantity` attribute: the physical-quantity reference, when
123    /// authored.
124    pub fn base_quantity(&self) -> ResourceResult<Option<EntityId>> {
125        self.record
126            .optional_ref("BaseQuantity", "IfcPhysicalQuantity")
127    }
128}