use ifc_model::{Entity, EntityId, Transaction, Value};
use crate::{AuthorError, AuthorResult};
#[derive(Debug, Clone, Copy, Default)]
#[non_exhaustive]
pub struct PersonDraft<'a> {
pub identification: Option<&'a str>,
pub family_name: Option<&'a str>,
pub given_name: Option<&'a str>,
}
impl<'a> PersonDraft<'a> {
#[must_use]
pub fn new() -> Self {
Self {
identification: None,
family_name: None,
given_name: None,
}
}
#[must_use]
pub fn identification(mut self, value: &'a str) -> Self {
self.identification = Some(value);
self
}
#[must_use]
pub fn family_name(mut self, value: &'a str) -> Self {
self.family_name = Some(value);
self
}
#[must_use]
pub fn given_name(mut self, value: &'a str) -> Self {
self.given_name = Some(value);
self
}
}
#[derive(Debug, Clone, Copy, Default)]
#[non_exhaustive]
pub struct OrganizationDraft<'a> {
pub identification: Option<&'a str>,
pub name: &'a str,
pub description: Option<&'a str>,
}
impl<'a> OrganizationDraft<'a> {
#[must_use]
pub fn new(name: &'a str) -> Self {
Self {
identification: None,
name,
description: None,
}
}
#[must_use]
pub fn identification(mut self, value: &'a str) -> Self {
self.identification = Some(value);
self
}
#[must_use]
pub fn description(mut self, value: &'a str) -> Self {
self.description = Some(value);
self
}
}
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub struct ApplicationDraft<'a> {
pub developer: EntityId,
pub version: &'a str,
pub full_name: &'a str,
pub identifier: &'a str,
}
impl<'a> ApplicationDraft<'a> {
#[must_use]
pub fn new(
developer: EntityId,
version: &'a str,
full_name: &'a str,
identifier: &'a str,
) -> Self {
Self {
developer,
version,
full_name,
identifier,
}
}
}
pub fn add_person(tx: &mut Transaction, draft: PersonDraft<'_>) -> AuthorResult<EntityId> {
if draft.identification.is_none() && draft.family_name.is_none() && draft.given_name.is_none() {
return Err(AuthorError::MissingRequired {
entity: "IFCPERSON".to_owned(),
attribute: "Identification|FamilyName|GivenName".to_owned(),
});
}
Ok(tx.create(Entity::new(
"IFCPERSON",
vec![
opt_text(draft.identification),
opt_text(draft.family_name),
opt_text(draft.given_name),
Value::Null,
Value::Null,
Value::Null,
Value::Null,
Value::Null,
],
)))
}
pub fn add_organization(
tx: &mut Transaction,
draft: OrganizationDraft<'_>,
) -> AuthorResult<EntityId> {
if draft.name.trim().is_empty() {
return Err(AuthorError::MissingRequired {
entity: "IFCORGANIZATION".to_owned(),
attribute: "Name".to_owned(),
});
}
Ok(tx.create(Entity::new(
"IFCORGANIZATION",
vec![
opt_text(draft.identification),
Value::Text(draft.name.into()),
opt_text(draft.description),
Value::Null,
Value::Null,
],
)))
}
pub fn add_person_and_organization(
tx: &mut Transaction,
person: EntityId,
organization: EntityId,
) -> EntityId {
tx.create(Entity::new(
"IFCPERSONANDORGANIZATION",
vec![Value::Ref(person), Value::Ref(organization), Value::Null],
))
}
pub fn add_application(
tx: &mut Transaction,
draft: ApplicationDraft<'_>,
) -> AuthorResult<EntityId> {
for (attribute, value) in [
("Version", draft.version),
("ApplicationFullName", draft.full_name),
("ApplicationIdentifier", draft.identifier),
] {
if value.trim().is_empty() {
return Err(AuthorError::MissingRequired {
entity: "IFCAPPLICATION".to_owned(),
attribute: attribute.to_owned(),
});
}
}
Ok(tx.create(Entity::new(
"IFCAPPLICATION",
vec![
Value::Ref(draft.developer),
Value::Text(draft.version.into()),
Value::Text(draft.full_name.into()),
Value::Text(draft.identifier.into()),
],
)))
}
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub struct OwnerHistoryDraft<'a> {
pub owning_user: EntityId,
pub owning_application: EntityId,
pub change_action: Option<&'a str>,
pub creation_date: i64,
pub last_modified_date: Option<i64>,
}
impl<'a> OwnerHistoryDraft<'a> {
#[must_use]
pub fn new(owning_user: EntityId, owning_application: EntityId, creation_date: i64) -> Self {
Self {
owning_user,
owning_application,
change_action: None,
creation_date,
last_modified_date: None,
}
}
#[must_use]
pub fn change_action(mut self, value: &'a str) -> Self {
self.change_action = Some(value);
self
}
#[must_use]
pub fn last_modified_date(mut self, value: i64) -> Self {
self.last_modified_date = Some(value);
self
}
}
const CHANGE_ACTIONS: &[&str] = &["NOCHANGE", "MODIFIED", "ADDED", "DELETED", "NOTDEFINED"];
pub fn add_owner_history(
tx: &mut Transaction,
draft: OwnerHistoryDraft<'_>,
) -> AuthorResult<EntityId> {
if let Some(action) = draft.change_action {
if !CHANGE_ACTIONS
.iter()
.any(|known| known.eq_ignore_ascii_case(action))
{
return Err(AuthorError::TypeMismatch {
entity: "IFCOWNERHISTORY".to_owned(),
attribute: "ChangeAction".to_owned(),
expected: "an IfcChangeActionEnum constant".to_owned(),
found: action.to_owned(),
});
}
}
if let Some(modified) = draft.last_modified_date {
if modified < draft.creation_date {
return Err(AuthorError::TypeMismatch {
entity: "IFCOWNERHISTORY".to_owned(),
attribute: "LastModifiedDate".to_owned(),
expected: "a timestamp at or after CreationDate".to_owned(),
found: modified.to_string(),
});
}
}
Ok(tx.create(Entity::new(
"IFCOWNERHISTORY",
vec![
Value::Ref(draft.owning_user),
Value::Ref(draft.owning_application),
Value::Null,
draft
.change_action
.map_or(Value::Null, |a| Value::Enum(a.into())),
draft.last_modified_date.map_or(Value::Null, Value::Integer),
Value::Null,
Value::Null,
Value::Integer(draft.creation_date),
],
)))
}
fn opt_text(value: Option<&str>) -> Value {
value.map_or(Value::Null, |v| Value::Text(v.into()))
}