use ifc_model::guid::Guid;
use ifc_model::{Entity, EntityId, Transaction, Value};
use crate::authoring::{ElementTypeError, ElementTypeResult};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SupertypeKind {
pub type_name: &'static str,
pub arity: usize,
}
pub const TYPE_OBJECT: SupertypeKind = SupertypeKind {
type_name: "IFCTYPEOBJECT",
arity: 6,
};
pub const TYPE_PRODUCT: SupertypeKind = SupertypeKind {
type_name: "IFCTYPEPRODUCT",
arity: 8,
};
pub const BUILT_ELEMENT_TYPE: SupertypeKind = SupertypeKind {
type_name: "IFCBUILTELEMENTTYPE",
arity: 9,
};
pub const CIVIL_ELEMENT_TYPE: SupertypeKind = SupertypeKind {
type_name: "IFCCIVILELEMENTTYPE",
arity: 9,
};
pub const DEEP_FOUNDATION_TYPE: SupertypeKind = SupertypeKind {
type_name: "IFCDEEPFOUNDATIONTYPE",
arity: 9,
};
pub const DISTRIBUTION_ELEMENT_TYPE: SupertypeKind = SupertypeKind {
type_name: "IFCDISTRIBUTIONELEMENTTYPE",
arity: 9,
};
pub const FURNISHING_ELEMENT_TYPE: SupertypeKind = SupertypeKind {
type_name: "IFCFURNISHINGELEMENTTYPE",
arity: 9,
};
pub const ALL_SUPERTYPES: &[SupertypeKind] = &[
TYPE_OBJECT,
TYPE_PRODUCT,
BUILT_ELEMENT_TYPE,
CIVIL_ELEMENT_TYPE,
DEEP_FOUNDATION_TYPE,
DISTRIBUTION_ELEMENT_TYPE,
FURNISHING_ELEMENT_TYPE,
];
#[derive(Debug, Clone, Copy, Default)]
pub struct SupertypeDraft<'a> {
pub description: Option<&'a str>,
pub applicable_occurrence: Option<&'a str>,
pub property_sets: &'a [(&'a str, EntityId)],
pub representation_maps: &'a [EntityId],
pub tag: Option<&'a str>,
pub element_type: Option<&'a str>,
}
fn invalid(
entity: &'static str,
attribute: &'static str,
value: impl Into<String>,
) -> ElementTypeError {
ElementTypeError::Invalid {
entity,
attribute,
value: value.into(),
}
}
fn text(value: Option<&str>) -> Value {
value.map_or(Value::Null, |v| Value::Text(v.into()))
}
pub fn create_supertype(
tx: &mut Transaction,
kind: SupertypeKind,
global_id: &str,
name: &str,
draft: SupertypeDraft<'_>,
) -> ElementTypeResult<EntityId> {
let entity = kind.type_name;
if Guid::parse(global_id).is_none() {
return Err(invalid(entity, "GlobalId", global_id));
}
if name.trim().is_empty() {
return Err(invalid(entity, "Name", "NameRequired"));
}
let mut attrs = vec![Value::Null; kind.arity];
attrs[0] = Value::Text(global_id.into());
attrs[2] = Value::Text(name.into());
attrs[3] = text(draft.description);
attrs[4] = text(draft.applicable_occurrence);
if !draft.property_sets.is_empty() {
for (index, (set_name, _)) in draft.property_sets.iter().enumerate() {
if set_name.trim().is_empty() {
return Err(invalid(entity, "HasPropertySets", "blank name"));
}
if draft.property_sets[..index]
.iter()
.any(|(seen, _)| seen == set_name)
{
return Err(invalid(entity, "HasPropertySets", (*set_name).to_owned()));
}
}
attrs[5] = Value::List(
draft
.property_sets
.iter()
.map(|(_, id)| Value::Ref(*id))
.collect(),
);
}
if !draft.representation_maps.is_empty() {
if kind.arity < 8 {
return Err(invalid(entity, "RepresentationMaps", "not declared"));
}
attrs[6] = Value::List(
draft
.representation_maps
.iter()
.copied()
.map(Value::Ref)
.collect(),
);
}
if draft.tag.is_some() {
if kind.arity < 8 {
return Err(invalid(entity, "Tag", "not declared"));
}
attrs[7] = text(draft.tag);
}
if draft.element_type.is_some() {
if kind.arity < 9 {
return Err(invalid(entity, "ElementType", "not declared"));
}
attrs[8] = text(draft.element_type);
}
Ok(tx.create(Entity::new(entity, attrs)))
}