1use std::sync::Arc;
5
6use ifc_model::{EntityId, Model, Transaction, Value};
7
8use super::{
9 optional_ref, optional_text, refs, require_accepts, require_enum, require_external_identity,
10 require_set, text, ClassificationDraft, ClassificationReferenceDraft, DocumentDraft,
11 DocumentReferenceDraft, LibraryDraft, LibraryReferenceDraft,
12};
13use crate::release::Release;
14use crate::{ClassificationError, ClassificationResult};
15
16fn optional_enum(value: Option<&str>) -> Value {
17 value.map_or(Value::Null, |v| Value::Enum(Arc::from(v)))
18}
19
20pub fn create_classification(
28 tx: &mut Transaction,
29 draft: ClassificationDraft<'_>,
30) -> ClassificationResult<EntityId> {
31 stage_classification(tx, Release::LEGACY, draft)
32}
33
34pub fn create_classification_in(
46 tx: &mut Transaction,
47 model: &Model,
48 draft: ClassificationDraft<'_>,
49) -> ClassificationResult<EntityId> {
50 stage_classification(tx, Release::of(model), draft)
51}
52
53fn stage_classification(
54 tx: &mut Transaction,
55 release: Release<'_>,
56 draft: ClassificationDraft<'_>,
57) -> ClassificationResult<EntityId> {
58 const ENTITY: &str = "IFCCLASSIFICATION";
59 release.require_entity(ENTITY)?;
60 if draft
61 .reference_tokens
62 .is_some_and(|tokens| tokens.is_empty())
63 {
64 return Err(ClassificationError::AuthoringInvalid {
65 entity: ENTITY,
66 attribute: "ReferenceTokens",
67 value: "empty LIST [1:?]".into(),
68 });
69 }
70 let tokens = draft.reference_tokens.map_or(Value::Null, |items| {
71 Value::List(items.iter().map(|v| text(v)).collect())
72 });
73 let record = release.record(
74 ENTITY,
75 vec![
76 ("Source", optional_text(draft.source)),
77 ("Edition", optional_text(draft.edition)),
78 ("EditionDate", optional_text(draft.edition_date)),
79 ("Name", text(draft.name)),
80 ("Description", optional_text(draft.description)),
81 ("Location", optional_text(draft.location)),
82 ("ReferenceTokens", tokens),
83 ],
84 )?;
85 Ok(tx.create(record))
86}
87
88pub fn create_classification_reference(
100 tx: &mut Transaction,
101 model: &Model,
102 draft: ClassificationReferenceDraft<'_>,
103) -> ClassificationResult<EntityId> {
104 const ENTITY: &str = "IFCCLASSIFICATIONREFERENCE";
105 let release = Release::of(model);
106 release.require_entity(ENTITY)?;
107 require_external_identity(ENTITY, draft.location, draft.identification, draft.name)?;
108 if let Some(source) = draft.referenced_source {
109 require_accepts(tx, model, release, ENTITY, "ReferencedSource", source)?;
110 }
111 let record = release.record(
112 ENTITY,
113 vec![
114 ("Location", optional_text(draft.location)),
115 ("Identification", optional_text(draft.identification)),
116 ("Name", optional_text(draft.name)),
117 ("ReferencedSource", optional_ref(draft.referenced_source)),
118 ("Description", optional_text(draft.description)),
119 ("Sort", optional_text(draft.sort)),
120 ],
121 )?;
122 Ok(tx.create(record))
123}
124
125pub fn create_document(
137 tx: &mut Transaction,
138 model: &Model,
139 draft: DocumentDraft<'_>,
140) -> ClassificationResult<EntityId> {
141 const ENTITY: &str = "IFCDOCUMENTINFORMATION";
142 let release = Release::of(model);
143 release.require_entity(ENTITY)?;
144 require_enum(release, ENTITY, "Confidentiality", draft.confidentiality)?;
145 require_enum(release, ENTITY, "Status", draft.status)?;
146 if let Some(owner) = draft.document_owner {
147 require_accepts(tx, model, release, ENTITY, "DocumentOwner", owner)?;
148 }
149 if let Some(editors) = draft.editors {
150 require_set(tx, model, release, ENTITY, "Editors", editors)?;
151 }
152 let record = release.record(
153 ENTITY,
154 vec![
155 ("Identification", text(draft.identification)),
156 ("Name", text(draft.name)),
157 ("Description", optional_text(draft.description)),
158 ("Location", optional_text(draft.location)),
159 ("Purpose", optional_text(draft.purpose)),
160 ("IntendedUse", optional_text(draft.intended_use)),
161 ("Scope", optional_text(draft.scope)),
162 ("Revision", optional_text(draft.revision)),
163 ("DocumentOwner", optional_ref(draft.document_owner)),
164 ("Editors", draft.editors.map_or(Value::Null, refs)),
165 ("CreationTime", optional_text(draft.creation_time)),
166 ("LastRevisionTime", optional_text(draft.last_revision_time)),
167 ("ElectronicFormat", optional_text(draft.electronic_format)),
168 ("ValidFrom", optional_text(draft.valid_from)),
169 ("ValidUntil", optional_text(draft.valid_until)),
170 ("Confidentiality", optional_enum(draft.confidentiality)),
171 ("Status", optional_enum(draft.status)),
172 ],
173 )?;
174 Ok(tx.create(record))
175}
176
177pub fn create_document_reference(
190 tx: &mut Transaction,
191 model: &Model,
192 draft: DocumentReferenceDraft<'_>,
193) -> ClassificationResult<EntityId> {
194 const ENTITY: &str = "IFCDOCUMENTREFERENCE";
195 let release = Release::of(model);
196 release.require_entity(ENTITY)?;
197 require_external_identity(ENTITY, draft.location, draft.identification, draft.name)?;
198 if !(draft.name.is_some() ^ draft.referenced_document.is_some()) {
199 return Err(ClassificationError::AuthoringInvalid {
200 entity: ENTITY,
201 attribute: "WR1",
202 value: "exactly one of Name and ReferencedDocument must be stated".into(),
203 });
204 }
205 if let Some(document) = draft.referenced_document {
206 require_accepts(tx, model, release, ENTITY, "ReferencedDocument", document)?;
207 }
208 let record = release.record(
209 ENTITY,
210 vec![
211 ("Location", optional_text(draft.location)),
212 ("Identification", optional_text(draft.identification)),
213 ("Name", optional_text(draft.name)),
214 ("Description", optional_text(draft.description)),
215 (
216 "ReferencedDocument",
217 optional_ref(draft.referenced_document),
218 ),
219 ],
220 )?;
221 Ok(tx.create(record))
222}
223
224pub fn create_library(
235 tx: &mut Transaction,
236 model: &Model,
237 draft: LibraryDraft<'_>,
238) -> ClassificationResult<EntityId> {
239 const ENTITY: &str = "IFCLIBRARYINFORMATION";
240 let release = Release::of(model);
241 release.require_entity(ENTITY)?;
242 if let Some(publisher) = draft.publisher {
243 require_accepts(tx, model, release, ENTITY, "Publisher", publisher)?;
244 }
245 let record = release.record(
246 ENTITY,
247 vec![
248 ("Name", text(draft.name)),
249 ("Version", optional_text(draft.version)),
250 ("Publisher", optional_ref(draft.publisher)),
251 ("VersionDate", optional_text(draft.version_date)),
252 ("Location", optional_text(draft.location)),
253 ("Description", optional_text(draft.description)),
254 ],
255 )?;
256 Ok(tx.create(record))
257}
258
259pub fn create_library_reference(
269 tx: &mut Transaction,
270 model: &Model,
271 draft: LibraryReferenceDraft<'_>,
272) -> ClassificationResult<EntityId> {
273 const ENTITY: &str = "IFCLIBRARYREFERENCE";
274 let release = Release::of(model);
275 release.require_entity(ENTITY)?;
276 require_external_identity(ENTITY, draft.location, draft.identification, draft.name)?;
277 if let Some(library) = draft.referenced_library {
278 require_accepts(tx, model, release, ENTITY, "ReferencedLibrary", library)?;
279 }
280 let record = release.record(
281 ENTITY,
282 vec![
283 ("Location", optional_text(draft.location)),
284 ("Identification", optional_text(draft.identification)),
285 ("Name", optional_text(draft.name)),
286 ("Description", optional_text(draft.description)),
287 ("Language", optional_text(draft.language)),
288 ("ReferencedLibrary", optional_ref(draft.referenced_library)),
289 ],
290 )?;
291 Ok(tx.create(record))
292}