use std::collections::HashSet;
use ifc_model::guid::Guid;
use ifc_model::{EntityId, Model, Transaction, Value};
use crate::authoring::{final_type, optional_ref, optional_text, refs, text, validate_target_in};
use crate::release::{bind, require_owner_history};
use crate::{ConstraintError, ConstraintResult};
const ASSIGNMENT: &str = "IFCRELASSOCIATESCONSTRAINT";
#[derive(Debug, Clone, Copy)]
pub struct ConstraintAssociationDraft<'a> {
pub global_id: &'a str,
pub name: Option<&'a str>,
pub description: Option<&'a str>,
pub related_objects: &'a [EntityId],
pub intent: Option<&'a str>,
pub relating_constraint: EntityId,
}
pub fn associate_constraint(
tx: &mut Transaction,
model: &Model,
draft: ConstraintAssociationDraft<'_>,
) -> ConstraintResult<EntityId> {
associate(tx, model, draft, None)
}
pub fn associate_constraint_with_owner_history(
tx: &mut Transaction,
model: &Model,
draft: ConstraintAssociationDraft<'_>,
owner_history: EntityId,
) -> ConstraintResult<EntityId> {
associate(tx, model, draft, Some(owner_history))
}
fn associate(
tx: &mut Transaction,
model: &Model,
draft: ConstraintAssociationDraft<'_>,
owner_history: Option<EntityId>,
) -> ConstraintResult<EntityId> {
let layout = bind(model)?;
if Guid::parse(draft.global_id).is_none() {
return Err(ConstraintError::AuthoringInvalid {
entity: ASSIGNMENT,
attribute: "GlobalId",
value: draft.global_id.into(),
});
}
validate_target_in(
layout.schema(),
tx,
model,
draft.relating_constraint,
"IfcConstraint",
)?;
if draft.related_objects.is_empty() {
return Err(ConstraintError::AuthoringInvalid {
entity: ASSIGNMENT,
attribute: "RelatedObjects",
value: "empty SET [1:?]".into(),
});
}
let mut seen = HashSet::new();
for &target in draft.related_objects {
if !seen.insert(target) {
return Err(ConstraintError::AuthoringInvalid {
entity: ASSIGNMENT,
attribute: "RelatedObjects",
value: format!("duplicate {target}"),
});
}
let actual =
final_type(tx, model, target).ok_or(ConstraintError::UnknownEntity { id: target })?;
if !layout.is_definition(actual) {
return Err(ConstraintError::AuthoringReferenceType {
target,
expected: "IfcDefinitionSelect",
actual: actual.into(),
});
}
}
if let Some(owner_history) = owner_history {
require_owner_history(tx, model, owner_history)?;
}
let record = layout.named_record(
ASSIGNMENT,
vec![
("GlobalId", text(draft.global_id)),
("OwnerHistory", optional_ref(owner_history)),
("Name", optional_text(draft.name)),
("Description", optional_text(draft.description)),
("RelatedObjects", refs(draft.related_objects)),
("Intent", optional_text(draft.intent)),
("RelatingConstraint", Value::Ref(draft.relating_constraint)),
],
)?;
Ok(tx.create(record))
}