use ifc_model::guid::Guid;
use ifc_model::{Entity, EntityId, Model, Transaction, Value};
use crate::table::Occurrence;
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum OccurrenceError {
MalformedGuid {
value: String,
},
UnknownPredefinedType {
entity: &'static str,
token: String,
},
NoPredefinedType {
entity: &'static str,
},
UserDefinedWithoutObjectType {
entity: &'static str,
},
WrongTypeClass {
entity: &'static str,
expected: &'static str,
found: String,
},
TypingNotPermitted {
entity: &'static str,
},
UnresolvedType {
id: EntityId,
},
}
pub type OccurrenceResult<T> = Result<T, OccurrenceError>;
#[derive(Debug, Clone, Copy, Default)]
pub struct OccurrenceDraft<'a> {
pub name: Option<&'a str>,
pub description: Option<&'a str>,
pub object_type: Option<&'a str>,
pub placement: Option<EntityId>,
pub representation: Option<EntityId>,
pub tag: Option<&'a str>,
}
fn text(v: Option<&str>) -> Value {
v.map_or(Value::Null, |s| Value::Text(s.into()))
}
fn slot_ref(v: Option<EntityId>) -> Value {
v.map_or(Value::Null, Value::Ref)
}
pub fn create(
tx: &mut Transaction,
model: &Model,
kind: Occurrence,
global_id: &str,
predefined_type: Option<&str>,
typed_by: Option<EntityId>,
draft: OccurrenceDraft<'_>,
) -> OccurrenceResult<EntityId> {
let entity = kind.type_name;
if Guid::parse(global_id).is_none() {
return Err(OccurrenceError::MalformedGuid {
value: global_id.into(),
});
}
if let Some(token) = predefined_type {
if kind.predefined_slot.is_none() {
return Err(OccurrenceError::NoPredefinedType { entity });
}
if !kind.members.contains(&token) {
return Err(OccurrenceError::UnknownPredefinedType {
entity,
token: token.into(),
});
}
if token == "USERDEFINED" && draft.object_type.is_none_or(|s| s.trim().is_empty()) {
return Err(OccurrenceError::UserDefinedWithoutObjectType { entity });
}
}
if let Some(id) = typed_by {
let Some(expected) = kind.type_class else {
return Err(OccurrenceError::TypingNotPermitted { entity });
};
let found = model
.get(id)
.ok_or(OccurrenceError::UnresolvedType { id })?
.type_name
.to_ascii_uppercase();
if found != expected {
return Err(OccurrenceError::WrongTypeClass {
entity,
expected,
found,
});
}
}
let mut attrs = vec![Value::Null; kind.arity];
attrs[0] = Value::Text(global_id.into());
attrs[2] = text(draft.name);
attrs[3] = text(draft.description);
attrs[4] = text(draft.object_type);
attrs[5] = slot_ref(draft.placement);
attrs[6] = slot_ref(draft.representation);
attrs[7] = text(draft.tag);
if let (Some(slot), Some(token)) = (kind.predefined_slot, predefined_type) {
attrs[slot] = Value::Enum(token.into());
}
Ok(tx.create(Entity::new(entity, attrs)))
}