ifc_resource/usage/
quantity.rs1use ifc_model::EntityId;
11
12use crate::error::{ResourceError, ResourceResult};
13use crate::view::Record;
14
15#[derive(Debug, Clone, Copy, PartialEq)]
17#[non_exhaustive]
18pub enum SimpleQuantityValue {
19 Area(f64),
21 Count(f64),
23 Length(f64),
25 Time(f64),
27 Volume(f64),
29 Weight(f64),
31}
32
33fn simple_kind_from_type(type_name: &str) -> Option<&'static str> {
34 if type_name.eq_ignore_ascii_case("IfcQuantityArea") {
35 Some("AreaValue")
36 } else if type_name.eq_ignore_ascii_case("IfcQuantityCount") {
37 Some("CountValue")
38 } else if type_name.eq_ignore_ascii_case("IfcQuantityLength") {
39 Some("LengthValue")
40 } else if type_name.eq_ignore_ascii_case("IfcQuantityTime") {
41 Some("TimeValue")
42 } else if type_name.eq_ignore_ascii_case("IfcQuantityVolume") {
43 Some("VolumeValue")
44 } else if type_name.eq_ignore_ascii_case("IfcQuantityWeight") {
45 Some("WeightValue")
46 } else {
47 None
48 }
49}
50
51fn simple_value(type_name: &str, raw: f64) -> SimpleQuantityValue {
52 if type_name.eq_ignore_ascii_case("IfcQuantityArea") {
53 SimpleQuantityValue::Area(raw)
54 } else if type_name.eq_ignore_ascii_case("IfcQuantityCount") {
55 SimpleQuantityValue::Count(raw)
56 } else if type_name.eq_ignore_ascii_case("IfcQuantityLength") {
57 SimpleQuantityValue::Length(raw)
58 } else if type_name.eq_ignore_ascii_case("IfcQuantityTime") {
59 SimpleQuantityValue::Time(raw)
60 } else if type_name.eq_ignore_ascii_case("IfcQuantityVolume") {
61 SimpleQuantityValue::Volume(raw)
62 } else {
63 SimpleQuantityValue::Weight(raw)
64 }
65}
66
67#[derive(Debug, Clone, Copy)]
72pub struct SimpleQuantity<'m, 's> {
73 record: Record<'m, 's>,
74 attribute: &'static str,
75}
76
77impl<'m, 's> SimpleQuantity<'m, 's> {
78 pub(crate) fn from_record(record: Record<'m, 's>) -> ResourceResult<Self> {
79 let attribute = simple_kind_from_type(&record.entity.type_name).ok_or_else(|| {
80 ResourceError::WrongType {
81 id: record.id,
82 expected: "concrete IfcPhysicalSimpleQuantity",
83 actual: record.entity.type_name.to_string(),
84 }
85 })?;
86 Ok(Self { record, attribute })
87 }
88
89 #[must_use]
91 pub fn id(&self) -> EntityId {
92 self.record.id
93 }
94
95 pub fn name(&self) -> ResourceResult<&'m str> {
97 self.record.required_text("Name")
98 }
99
100 pub fn description(&self) -> ResourceResult<Option<&'m str>> {
102 self.record.optional_text("Description")
103 }
104
105 pub fn unit(&self) -> ResourceResult<Option<EntityId>> {
108 self.record.optional_ref("Unit", "IfcNamedUnit")
109 }
110
111 pub fn formula(&self) -> ResourceResult<Option<&'m str>> {
113 self.record.optional_text("Formula")
114 }
115
116 pub fn value(&self) -> ResourceResult<SimpleQuantityValue> {
118 let raw = self.record.required_non_negative_number(self.attribute)?;
119 Ok(simple_value(&self.record.entity.type_name, raw))
120 }
121}
122
123#[derive(Debug, Clone, Copy)]
131pub struct ComplexQuantity<'m, 's> {
132 record: Record<'m, 's>,
133}
134
135impl<'m, 's> ComplexQuantity<'m, 's> {
136 pub(crate) fn from_record(record: Record<'m, 's>) -> ResourceResult<Self> {
137 let quantity = Self { record };
138 let members = quantity.member_ids()?;
139 if members.contains(&quantity.record.id) {
140 return Err(ResourceError::SemanticViolation {
141 entity: Some(quantity.record.id),
142 rule: "IfcPhysicalComplexQuantity.NoSelfReference",
143 });
144 }
145 Ok(quantity)
146 }
147
148 #[must_use]
150 pub fn id(&self) -> EntityId {
151 self.record.id
152 }
153
154 pub fn name(&self) -> ResourceResult<&'m str> {
156 self.record.required_text("Name")
157 }
158
159 pub fn description(&self) -> ResourceResult<Option<&'m str>> {
161 self.record.optional_text("Description")
162 }
163
164 pub fn discrimination(&self) -> ResourceResult<&'m str> {
166 self.record.required_text("Discrimination")
167 }
168
169 pub fn quality(&self) -> ResourceResult<Option<&'m str>> {
171 self.record.optional_text("Quality")
172 }
173
174 pub fn usage(&self) -> ResourceResult<Option<&'m str>> {
176 self.record.optional_text("Usage")
177 }
178
179 pub fn member_ids(&self) -> ResourceResult<Vec<EntityId>> {
181 self.record
182 .refs("HasQuantities", "IfcPhysicalQuantity", 1, false, true)
183 }
184}