ifc_resource/resource/
type.rs1use ifc_model::EntityId;
4
5use crate::error::{ResourceError, ResourceResult};
6use crate::view::Record;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12#[non_exhaustive]
13pub enum ResourceTypeKind {
14 Labor,
16 Equipment,
18 Crew,
20 Material,
22 Product,
24 Subcontract,
26}
27
28impl ResourceTypeKind {
29 fn from_type(type_name: &str) -> Option<Self> {
30 if type_name.eq_ignore_ascii_case("IfcLaborResourceType") {
31 Some(Self::Labor)
32 } else if type_name.eq_ignore_ascii_case("IfcConstructionEquipmentResourceType") {
33 Some(Self::Equipment)
34 } else if type_name.eq_ignore_ascii_case("IfcCrewResourceType") {
35 Some(Self::Crew)
36 } else if type_name.eq_ignore_ascii_case("IfcConstructionMaterialResourceType") {
37 Some(Self::Material)
38 } else if type_name.eq_ignore_ascii_case("IfcConstructionProductResourceType") {
39 Some(Self::Product)
40 } else if type_name.eq_ignore_ascii_case("IfcSubContractResourceType") {
41 Some(Self::Subcontract)
42 } else {
43 None
44 }
45 }
46}
47
48#[derive(Debug, Clone, Copy)]
49pub struct ConstructionResourceType<'m, 's> {
52 record: Record<'m, 's>,
53 kind: ResourceTypeKind,
54}
55
56impl<'m, 's> ConstructionResourceType<'m, 's> {
57 pub(crate) fn from_record(record: Record<'m, 's>) -> ResourceResult<Self> {
58 let kind = ResourceTypeKind::from_type(&record.entity.type_name).ok_or_else(|| {
59 ResourceError::WrongType {
60 id: record.id,
61 expected: "concrete IfcConstructionResourceType occurrence",
62 actual: record.entity.type_name.to_string(),
63 }
64 })?;
65 Ok(Self { record, kind })
66 }
67
68 #[must_use]
70 pub fn id(&self) -> EntityId {
71 self.record.id
72 }
73
74 #[must_use]
76 pub fn kind(&self) -> ResourceTypeKind {
77 self.kind
78 }
79
80 pub fn name(&self) -> ResourceResult<&'m str> {
82 self.record.required_text("Name")
83 }
84
85 pub fn identification(&self) -> ResourceResult<Option<&'m str>> {
87 self.record.optional_text("Identification")
88 }
89
90 pub fn long_description(&self) -> ResourceResult<Option<&'m str>> {
92 self.record.optional_text("LongDescription")
93 }
94
95 pub fn resource_type(&self) -> ResourceResult<Option<&'m str>> {
97 self.record.optional_text("ResourceType")
98 }
99
100 pub fn predefined_type(&self) -> ResourceResult<Option<&'m str>> {
104 let value = self.record.optional_enum("PredefinedType")?;
105 if value.is_some_and(|value| value.eq_ignore_ascii_case("USERDEFINED"))
106 && self
107 .record
108 .optional_text("ResourceType")?
109 .is_none_or(|value| value.trim().is_empty())
110 {
111 return Err(ResourceError::SemanticViolation {
112 entity: Some(self.record.id),
113 rule: "CorrectPredefinedType requires ResourceType for USERDEFINED",
114 });
115 }
116 Ok(value)
117 }
118
119 pub fn base_costs(&self) -> ResourceResult<Vec<EntityId>> {
121 self.record
122 .refs("BaseCosts", "IfcAppliedValue", 1, true, false)
123 }
124
125 pub fn base_quantity(&self) -> ResourceResult<Option<EntityId>> {
128 self.record
129 .optional_ref("BaseQuantity", "IfcPhysicalQuantity")
130 }
131}
132
133impl<'m, 's> crate::view::ResourceView<'m, 's> {
134 pub fn resource_type(&self, id: EntityId) -> ResourceResult<ConstructionResourceType<'m, 's>> {
136 ConstructionResourceType::from_record(self.record(id, "IfcConstructionResourceType")?)
137 }
138
139 pub fn assigned_resource_type(&self, occurrence: EntityId) -> ResourceResult<Option<EntityId>> {
147 self.resource(occurrence)?;
148 let mut found = None;
149 for relation in self.ids_of_ancestor("IfcRelDefinesByType") {
150 let record = self.record(relation, "IfcRelDefinesByType")?;
151 let related = record.refs("RelatedObjects", "IfcObject", 1, false, true)?;
152 if !related.contains(&occurrence) {
153 continue;
154 }
155 let relating_type =
156 record.required_ref("RelatingType", "IfcConstructionResourceType")?;
157 if let Some(existing) = found.replace(relating_type) {
158 if existing != relating_type {
159 return Err(ResourceError::SemanticViolation {
160 entity: Some(occurrence),
161 rule: "IfcTypeObject.Types permits at most one type assignment",
162 });
163 }
164 }
165 }
166 Ok(found)
167 }
168}