use std::sync::Arc;
use ifc_model::{EntityId, Model};
use crate::error::PropertyAnomaly;
use crate::nesting::Nesting;
use crate::pset::complex::complex_members;
use crate::value::MeasureValue;
const NAME: usize = 0;
const DESCRIPTION: usize = 1;
#[derive(Debug, Clone, PartialEq)]
pub enum PropertyValue {
Single {
value: Option<MeasureValue>,
unit: Option<EntityId>,
},
Enumerated {
values: Vec<MeasureValue>,
enumeration: Option<EntityId>,
},
Bounded {
upper: Option<MeasureValue>,
lower: Option<MeasureValue>,
set_point: Option<MeasureValue>,
unit: Option<EntityId>,
},
List {
values: Vec<MeasureValue>,
unit: Option<EntityId>,
},
Table {
defining: Vec<MeasureValue>,
defined: Vec<MeasureValue>,
interpolation: Option<Arc<str>>,
},
Reference {
usage: Option<Arc<str>>,
reference: Option<EntityId>,
},
Complex {
usage: Option<Arc<str>>,
properties: Vec<Property>,
},
Unsupported {
type_name: Arc<str>,
},
}
#[derive(Debug, Clone, PartialEq)]
pub struct Property {
pub id: EntityId,
pub name: Option<Arc<str>>,
pub description: Option<Arc<str>>,
pub value: PropertyValue,
}
pub fn property(model: &Model, id: EntityId) -> Option<Property> {
property_checked(model, id).map(|(property, _)| property)
}
pub fn property_checked(model: &Model, id: EntityId) -> Option<(Property, Vec<PropertyAnomaly>)> {
let mut anomalies = Vec::new();
let property = read_property(model, id, &mut Nesting::new(&mut anomalies))?;
Some((property, anomalies))
}
pub(crate) fn read_property(
model: &Model,
id: EntityId,
nesting: &mut Nesting<'_>,
) -> Option<Property> {
let entity = model.get(id)?;
let ty = entity.type_name.to_ascii_uppercase();
let name = entity.attributes.get(NAME).and_then(text);
let description = entity.attributes.get(DESCRIPTION).and_then(text);
let value = match ty.as_str() {
"IFCPROPERTYSINGLEVALUE" => PropertyValue::Single {
value: entity.attributes.get(2).and_then(MeasureValue::read),
unit: entity.attributes.get(3).and_then(entity_ref),
},
"IFCPROPERTYENUMERATEDVALUE" => PropertyValue::Enumerated {
values: entity
.attributes
.get(2)
.and_then(MeasureValue::read_list)
.unwrap_or_default(),
enumeration: entity.attributes.get(3).and_then(entity_ref),
},
"IFCPROPERTYBOUNDEDVALUE" => PropertyValue::Bounded {
upper: entity.attributes.get(2).and_then(MeasureValue::read),
lower: entity.attributes.get(3).and_then(MeasureValue::read),
unit: entity.attributes.get(4).and_then(entity_ref),
set_point: entity.attributes.get(5).and_then(MeasureValue::read),
},
"IFCPROPERTYLISTVALUE" => PropertyValue::List {
values: entity
.attributes
.get(2)
.and_then(MeasureValue::read_list)
.unwrap_or_default(),
unit: entity.attributes.get(3).and_then(entity_ref),
},
"IFCPROPERTYTABLEVALUE" => PropertyValue::Table {
defining: entity
.attributes
.get(2)
.and_then(MeasureValue::read_list)
.unwrap_or_default(),
defined: entity
.attributes
.get(3)
.and_then(MeasureValue::read_list)
.unwrap_or_default(),
interpolation: entity.attributes.get(7).and_then(enum_text),
},
"IFCPROPERTYREFERENCEVALUE" => PropertyValue::Reference {
usage: entity.attributes.get(2).and_then(text),
reference: entity.attributes.get(3).and_then(entity_ref),
},
"IFCCOMPLEXPROPERTY" => PropertyValue::Complex {
usage: entity.attributes.get(2).and_then(text),
properties: complex_members(model, id, entity.attributes.get(3), nesting),
},
_ => PropertyValue::Unsupported {
type_name: ty.as_str().into(),
},
};
Some(Property {
id,
name,
description,
value,
})
}
fn enum_text(value: &ifc_model::Value) -> Option<Arc<str>> {
match value.unwrap_typed() {
ifc_model::Value::Enum(t) | ifc_model::Value::Text(t) => Some(t.clone()),
_ => None,
}
}
fn text(value: &ifc_model::Value) -> Option<Arc<str>> {
match value.unwrap_typed() {
ifc_model::Value::Text(t) => Some(t.clone()),
_ => None,
}
}
fn entity_ref(value: &ifc_model::Value) -> Option<EntityId> {
match value.unwrap_typed() {
ifc_model::Value::Ref(id) => Some(*id),
_ => None,
}
}