use ifc_model::{EntityId, Model, Transaction, Value};
use crate::error::PropertyError;
use crate::quantity::set::QuantityKind;
const SIMPLE_VALUE_SLOT: usize = 3;
pub fn set_quantity_value(
tx: &mut Transaction,
model: &Model,
id: EntityId,
value: f64,
) -> Result<(), PropertyError> {
let entity = model.get(id).ok_or(PropertyError::MissingEntity { id })?;
let kind = QuantityKind::from_type_name(&entity.type_name).ok_or_else(|| {
PropertyError::NotAQuantity {
id,
type_name: entity.type_name.to_string(),
}
})?;
let measure = entity
.attribute(SIMPLE_VALUE_SLOT)
.and_then(declared_measure)
.unwrap_or_else(|| kind.measure_type().to_string());
let numeric = if kind == QuantityKind::Count {
Value::Integer(value as i64)
} else {
Value::Real(value)
};
tx.set_attribute(
id,
SIMPLE_VALUE_SLOT,
Value::Typed {
type_name: measure.into(),
value: Box::new(numeric),
},
);
Ok(())
}
pub fn set_name(
tx: &mut Transaction,
model: &Model,
id: EntityId,
name: &str,
) -> Result<(), PropertyError> {
if model.get(id).is_none() {
return Err(PropertyError::MissingEntity { id });
}
tx.set_attribute(id, 0, Value::Text(name.into()));
Ok(())
}
pub fn set_description(
tx: &mut Transaction,
model: &Model,
id: EntityId,
description: Option<&str>,
) -> Result<(), PropertyError> {
if model.get(id).is_none() {
return Err(PropertyError::MissingEntity { id });
}
let value = match description {
Some(text) => Value::Text(text.into()),
None => Value::Null,
};
tx.set_attribute(id, 1, value);
Ok(())
}
pub fn create_quantity(
tx: &mut Transaction,
kind: QuantityKind,
name: &str,
value: f64,
) -> EntityId {
create_quantity_with(tx, kind, name, value, QuantityExtras::default())
}
#[derive(Debug, Clone, Copy, Default)]
pub struct QuantityExtras<'a> {
pub description: Option<&'a str>,
pub unit: Option<EntityId>,
pub formula: Option<&'a str>,
}
#[must_use]
pub fn create_quantity_with(
tx: &mut Transaction,
kind: QuantityKind,
name: &str,
value: f64,
extras: QuantityExtras<'_>,
) -> EntityId {
let numeric = if kind == QuantityKind::Count {
Value::Integer(value as i64)
} else {
Value::Real(value)
};
tx.create(ifc_model::Entity::new(
kind.type_name(),
vec![
Value::Text(name.into()),
extras
.description
.map_or(Value::Null, |text| Value::Text(text.into())),
extras.unit.map_or(Value::Null, Value::Ref),
Value::Typed {
type_name: kind.measure_type().into(),
value: Box::new(numeric),
},
extras
.formula
.map_or(Value::Null, |text| Value::Text(text.into())),
],
))
}
pub fn add_quantity_to_set(
tx: &mut Transaction,
model: &Model,
set: EntityId,
quantities: &[EntityId],
) -> Result<(), PropertyError> {
let entity = model
.get(set)
.ok_or(PropertyError::MissingEntity { id: set })?;
if !entity.type_name.eq_ignore_ascii_case("IFCELEMENTQUANTITY") {
return Err(PropertyError::NotAQuantitySet {
id: set,
type_name: entity.type_name.to_string(),
});
}
const QUANTITIES_SLOT: usize = 5;
let mut members: Vec<Value> = entity
.attribute(QUANTITIES_SLOT)
.and_then(Value::as_list)
.map(<[Value]>::to_vec)
.unwrap_or_default();
let existing: Vec<EntityId> = members.iter().filter_map(Value::as_ref_id).collect();
for id in quantities {
if !existing.contains(id) {
members.push(Value::Ref(*id));
}
}
tx.set_attribute(set, QUANTITIES_SLOT, Value::List(members));
Ok(())
}
fn declared_measure(value: &Value) -> Option<String> {
match value {
Value::Typed { type_name, .. } => Some(type_name.to_string()),
_ => None,
}
}