use ifc_model::EntityId;
use crate::error::{ResourceError, ResourceResult};
use crate::view::Record;
#[derive(Debug, Clone, Copy, PartialEq)]
#[non_exhaustive]
pub enum SimpleQuantityValue {
Area(f64),
Count(f64),
Length(f64),
Time(f64),
Volume(f64),
Weight(f64),
}
fn simple_kind_from_type(type_name: &str) -> Option<&'static str> {
if type_name.eq_ignore_ascii_case("IfcQuantityArea") {
Some("AreaValue")
} else if type_name.eq_ignore_ascii_case("IfcQuantityCount") {
Some("CountValue")
} else if type_name.eq_ignore_ascii_case("IfcQuantityLength") {
Some("LengthValue")
} else if type_name.eq_ignore_ascii_case("IfcQuantityTime") {
Some("TimeValue")
} else if type_name.eq_ignore_ascii_case("IfcQuantityVolume") {
Some("VolumeValue")
} else if type_name.eq_ignore_ascii_case("IfcQuantityWeight") {
Some("WeightValue")
} else {
None
}
}
fn simple_value(type_name: &str, raw: f64) -> SimpleQuantityValue {
if type_name.eq_ignore_ascii_case("IfcQuantityArea") {
SimpleQuantityValue::Area(raw)
} else if type_name.eq_ignore_ascii_case("IfcQuantityCount") {
SimpleQuantityValue::Count(raw)
} else if type_name.eq_ignore_ascii_case("IfcQuantityLength") {
SimpleQuantityValue::Length(raw)
} else if type_name.eq_ignore_ascii_case("IfcQuantityTime") {
SimpleQuantityValue::Time(raw)
} else if type_name.eq_ignore_ascii_case("IfcQuantityVolume") {
SimpleQuantityValue::Volume(raw)
} else {
SimpleQuantityValue::Weight(raw)
}
}
#[derive(Debug, Clone, Copy)]
pub struct SimpleQuantity<'m, 's> {
record: Record<'m, 's>,
attribute: &'static str,
}
impl<'m, 's> SimpleQuantity<'m, 's> {
pub(crate) fn from_record(record: Record<'m, 's>) -> ResourceResult<Self> {
let attribute = simple_kind_from_type(&record.entity.type_name).ok_or_else(|| {
ResourceError::WrongType {
id: record.id,
expected: "concrete IfcPhysicalSimpleQuantity",
actual: record.entity.type_name.to_string(),
}
})?;
Ok(Self { record, attribute })
}
#[must_use]
pub fn id(&self) -> EntityId {
self.record.id
}
pub fn name(&self) -> ResourceResult<&'m str> {
self.record.required_text("Name")
}
pub fn description(&self) -> ResourceResult<Option<&'m str>> {
self.record.optional_text("Description")
}
pub fn unit(&self) -> ResourceResult<Option<EntityId>> {
self.record.optional_ref("Unit", "IfcNamedUnit")
}
pub fn formula(&self) -> ResourceResult<Option<&'m str>> {
self.record.optional_text("Formula")
}
pub fn value(&self) -> ResourceResult<SimpleQuantityValue> {
let raw = self.record.required_non_negative_number(self.attribute)?;
Ok(simple_value(&self.record.entity.type_name, raw))
}
}
#[derive(Debug, Clone, Copy)]
pub struct ComplexQuantity<'m, 's> {
record: Record<'m, 's>,
}
impl<'m, 's> ComplexQuantity<'m, 's> {
pub(crate) fn from_record(record: Record<'m, 's>) -> ResourceResult<Self> {
let quantity = Self { record };
let members = quantity.member_ids()?;
if members.contains(&quantity.record.id) {
return Err(ResourceError::SemanticViolation {
entity: Some(quantity.record.id),
rule: "IfcPhysicalComplexQuantity.NoSelfReference",
});
}
Ok(quantity)
}
#[must_use]
pub fn id(&self) -> EntityId {
self.record.id
}
pub fn name(&self) -> ResourceResult<&'m str> {
self.record.required_text("Name")
}
pub fn description(&self) -> ResourceResult<Option<&'m str>> {
self.record.optional_text("Description")
}
pub fn discrimination(&self) -> ResourceResult<&'m str> {
self.record.required_text("Discrimination")
}
pub fn quality(&self) -> ResourceResult<Option<&'m str>> {
self.record.optional_text("Quality")
}
pub fn usage(&self) -> ResourceResult<Option<&'m str>> {
self.record.optional_text("Usage")
}
pub fn member_ids(&self) -> ResourceResult<Vec<EntityId>> {
self.record
.refs("HasQuantities", "IfcPhysicalQuantity", 1, false, true)
}
}