ifc_constraint/
association.rs1use std::collections::HashSet;
8
9use ifc_model::guid::Guid;
10use ifc_model::{EntityId, Model, Transaction, Value};
11
12use crate::authoring::{final_type, optional_ref, optional_text, refs, text, validate_target_in};
13use crate::release::{bind, require_owner_history};
14use crate::{ConstraintError, ConstraintResult};
15
16const ASSIGNMENT: &str = "IFCRELASSOCIATESCONSTRAINT";
17
18#[derive(Debug, Clone, Copy)]
20pub struct ConstraintAssociationDraft<'a> {
21 pub global_id: &'a str,
23 pub name: Option<&'a str>,
25 pub description: Option<&'a str>,
27 pub related_objects: &'a [EntityId],
29 pub intent: Option<&'a str>,
31 pub relating_constraint: EntityId,
33}
34
35pub fn associate_constraint(
57 tx: &mut Transaction,
58 model: &Model,
59 draft: ConstraintAssociationDraft<'_>,
60) -> ConstraintResult<EntityId> {
61 associate(tx, model, draft, None)
62}
63
64pub fn associate_constraint_with_owner_history(
80 tx: &mut Transaction,
81 model: &Model,
82 draft: ConstraintAssociationDraft<'_>,
83 owner_history: EntityId,
84) -> ConstraintResult<EntityId> {
85 associate(tx, model, draft, Some(owner_history))
86}
87
88fn associate(
90 tx: &mut Transaction,
91 model: &Model,
92 draft: ConstraintAssociationDraft<'_>,
93 owner_history: Option<EntityId>,
94) -> ConstraintResult<EntityId> {
95 let layout = bind(model)?;
96 if Guid::parse(draft.global_id).is_none() {
97 return Err(ConstraintError::AuthoringInvalid {
98 entity: ASSIGNMENT,
99 attribute: "GlobalId",
100 value: draft.global_id.into(),
101 });
102 }
103 validate_target_in(
104 layout.schema(),
105 tx,
106 model,
107 draft.relating_constraint,
108 "IfcConstraint",
109 )?;
110 if draft.related_objects.is_empty() {
111 return Err(ConstraintError::AuthoringInvalid {
112 entity: ASSIGNMENT,
113 attribute: "RelatedObjects",
114 value: "empty SET [1:?]".into(),
115 });
116 }
117 let mut seen = HashSet::new();
118 for &target in draft.related_objects {
119 if !seen.insert(target) {
120 return Err(ConstraintError::AuthoringInvalid {
121 entity: ASSIGNMENT,
122 attribute: "RelatedObjects",
123 value: format!("duplicate {target}"),
124 });
125 }
126 let actual =
127 final_type(tx, model, target).ok_or(ConstraintError::UnknownEntity { id: target })?;
128 if !layout.is_definition(actual) {
129 return Err(ConstraintError::AuthoringReferenceType {
130 target,
131 expected: "IfcDefinitionSelect",
132 actual: actual.into(),
133 });
134 }
135 }
136 if let Some(owner_history) = owner_history {
137 require_owner_history(tx, model, owner_history)?;
138 }
139 let record = layout.named_record(
140 ASSIGNMENT,
141 vec![
142 ("GlobalId", text(draft.global_id)),
143 ("OwnerHistory", optional_ref(owner_history)),
144 ("Name", optional_text(draft.name)),
145 ("Description", optional_text(draft.description)),
146 ("RelatedObjects", refs(draft.related_objects)),
147 ("Intent", optional_text(draft.intent)),
148 ("RelatingConstraint", Value::Ref(draft.relating_constraint)),
149 ],
150 )?;
151 Ok(tx.create(record))
152}