use std::sync::Arc;
use ifc_model::{EntityId, Model, Transaction, Value};
use super::{
optional_ref, optional_text, refs, require_accepts, require_enum, require_external_identity,
require_set, text, ClassificationDraft, ClassificationReferenceDraft, DocumentDraft,
DocumentReferenceDraft, LibraryDraft, LibraryReferenceDraft,
};
use crate::release::Release;
use crate::{ClassificationError, ClassificationResult};
fn optional_enum(value: Option<&str>) -> Value {
value.map_or(Value::Null, |v| Value::Enum(Arc::from(v)))
}
pub fn create_classification(
tx: &mut Transaction,
draft: ClassificationDraft<'_>,
) -> ClassificationResult<EntityId> {
stage_classification(tx, Release::LEGACY, draft)
}
pub fn create_classification_in(
tx: &mut Transaction,
model: &Model,
draft: ClassificationDraft<'_>,
) -> ClassificationResult<EntityId> {
stage_classification(tx, Release::of(model), draft)
}
fn stage_classification(
tx: &mut Transaction,
release: Release<'_>,
draft: ClassificationDraft<'_>,
) -> ClassificationResult<EntityId> {
const ENTITY: &str = "IFCCLASSIFICATION";
release.require_entity(ENTITY)?;
if draft
.reference_tokens
.is_some_and(|tokens| tokens.is_empty())
{
return Err(ClassificationError::AuthoringInvalid {
entity: ENTITY,
attribute: "ReferenceTokens",
value: "empty LIST [1:?]".into(),
});
}
let tokens = draft.reference_tokens.map_or(Value::Null, |items| {
Value::List(items.iter().map(|v| text(v)).collect())
});
let record = release.record(
ENTITY,
vec![
("Source", optional_text(draft.source)),
("Edition", optional_text(draft.edition)),
("EditionDate", optional_text(draft.edition_date)),
("Name", text(draft.name)),
("Description", optional_text(draft.description)),
("Location", optional_text(draft.location)),
("ReferenceTokens", tokens),
],
)?;
Ok(tx.create(record))
}
pub fn create_classification_reference(
tx: &mut Transaction,
model: &Model,
draft: ClassificationReferenceDraft<'_>,
) -> ClassificationResult<EntityId> {
const ENTITY: &str = "IFCCLASSIFICATIONREFERENCE";
let release = Release::of(model);
release.require_entity(ENTITY)?;
require_external_identity(ENTITY, draft.location, draft.identification, draft.name)?;
if let Some(source) = draft.referenced_source {
require_accepts(tx, model, release, ENTITY, "ReferencedSource", source)?;
}
let record = release.record(
ENTITY,
vec![
("Location", optional_text(draft.location)),
("Identification", optional_text(draft.identification)),
("Name", optional_text(draft.name)),
("ReferencedSource", optional_ref(draft.referenced_source)),
("Description", optional_text(draft.description)),
("Sort", optional_text(draft.sort)),
],
)?;
Ok(tx.create(record))
}
pub fn create_document(
tx: &mut Transaction,
model: &Model,
draft: DocumentDraft<'_>,
) -> ClassificationResult<EntityId> {
const ENTITY: &str = "IFCDOCUMENTINFORMATION";
let release = Release::of(model);
release.require_entity(ENTITY)?;
require_enum(release, ENTITY, "Confidentiality", draft.confidentiality)?;
require_enum(release, ENTITY, "Status", draft.status)?;
if let Some(owner) = draft.document_owner {
require_accepts(tx, model, release, ENTITY, "DocumentOwner", owner)?;
}
if let Some(editors) = draft.editors {
require_set(tx, model, release, ENTITY, "Editors", editors)?;
}
let record = release.record(
ENTITY,
vec![
("Identification", text(draft.identification)),
("Name", text(draft.name)),
("Description", optional_text(draft.description)),
("Location", optional_text(draft.location)),
("Purpose", optional_text(draft.purpose)),
("IntendedUse", optional_text(draft.intended_use)),
("Scope", optional_text(draft.scope)),
("Revision", optional_text(draft.revision)),
("DocumentOwner", optional_ref(draft.document_owner)),
("Editors", draft.editors.map_or(Value::Null, refs)),
("CreationTime", optional_text(draft.creation_time)),
("LastRevisionTime", optional_text(draft.last_revision_time)),
("ElectronicFormat", optional_text(draft.electronic_format)),
("ValidFrom", optional_text(draft.valid_from)),
("ValidUntil", optional_text(draft.valid_until)),
("Confidentiality", optional_enum(draft.confidentiality)),
("Status", optional_enum(draft.status)),
],
)?;
Ok(tx.create(record))
}
pub fn create_document_reference(
tx: &mut Transaction,
model: &Model,
draft: DocumentReferenceDraft<'_>,
) -> ClassificationResult<EntityId> {
const ENTITY: &str = "IFCDOCUMENTREFERENCE";
let release = Release::of(model);
release.require_entity(ENTITY)?;
require_external_identity(ENTITY, draft.location, draft.identification, draft.name)?;
if !(draft.name.is_some() ^ draft.referenced_document.is_some()) {
return Err(ClassificationError::AuthoringInvalid {
entity: ENTITY,
attribute: "WR1",
value: "exactly one of Name and ReferencedDocument must be stated".into(),
});
}
if let Some(document) = draft.referenced_document {
require_accepts(tx, model, release, ENTITY, "ReferencedDocument", document)?;
}
let record = release.record(
ENTITY,
vec![
("Location", optional_text(draft.location)),
("Identification", optional_text(draft.identification)),
("Name", optional_text(draft.name)),
("Description", optional_text(draft.description)),
(
"ReferencedDocument",
optional_ref(draft.referenced_document),
),
],
)?;
Ok(tx.create(record))
}
pub fn create_library(
tx: &mut Transaction,
model: &Model,
draft: LibraryDraft<'_>,
) -> ClassificationResult<EntityId> {
const ENTITY: &str = "IFCLIBRARYINFORMATION";
let release = Release::of(model);
release.require_entity(ENTITY)?;
if let Some(publisher) = draft.publisher {
require_accepts(tx, model, release, ENTITY, "Publisher", publisher)?;
}
let record = release.record(
ENTITY,
vec![
("Name", text(draft.name)),
("Version", optional_text(draft.version)),
("Publisher", optional_ref(draft.publisher)),
("VersionDate", optional_text(draft.version_date)),
("Location", optional_text(draft.location)),
("Description", optional_text(draft.description)),
],
)?;
Ok(tx.create(record))
}
pub fn create_library_reference(
tx: &mut Transaction,
model: &Model,
draft: LibraryReferenceDraft<'_>,
) -> ClassificationResult<EntityId> {
const ENTITY: &str = "IFCLIBRARYREFERENCE";
let release = Release::of(model);
release.require_entity(ENTITY)?;
require_external_identity(ENTITY, draft.location, draft.identification, draft.name)?;
if let Some(library) = draft.referenced_library {
require_accepts(tx, model, release, ENTITY, "ReferencedLibrary", library)?;
}
let record = release.record(
ENTITY,
vec![
("Location", optional_text(draft.location)),
("Identification", optional_text(draft.identification)),
("Name", optional_text(draft.name)),
("Description", optional_text(draft.description)),
("Language", optional_text(draft.language)),
("ReferencedLibrary", optional_ref(draft.referenced_library)),
],
)?;
Ok(tx.create(record))
}