ifc_spatial/authoring/
external.rs1use ifc_model::guid::Guid;
17use ifc_model::{Entity, EntityId, Transaction, Value};
18
19use super::{invalid, optional_text, SpatialAuthoringResult};
20
21const EXTERNAL_KIND: &[&str] = &[
26 "EXTERNAL",
27 "EXTERNAL_EARTH",
28 "EXTERNAL_FIRE",
29 "EXTERNAL_WATER",
30 "USERDEFINED",
31 "NOTDEFINED",
32];
33
34#[derive(Debug, Clone, Copy, Default)]
39#[non_exhaustive]
40pub struct ExternalSpatialDraft<'a> {
41 pub name: Option<&'a str>,
43 pub description: Option<&'a str>,
45 pub object_type: Option<&'a str>,
47 pub placement: Option<EntityId>,
49 pub representation: Option<EntityId>,
51 pub long_name: Option<&'a str>,
53 pub predefined_type: Option<&'a str>,
55}
56
57impl<'a> ExternalSpatialDraft<'a> {
58 #[must_use]
60 pub fn new() -> Self {
61 Self::default()
62 }
63
64 #[must_use]
66 pub fn name(mut self, value: &'a str) -> Self {
67 self.name = Some(value);
68 self
69 }
70
71 #[must_use]
73 pub fn description(mut self, value: &'a str) -> Self {
74 self.description = Some(value);
75 self
76 }
77
78 #[must_use]
80 pub fn object_type(mut self, value: &'a str) -> Self {
81 self.object_type = Some(value);
82 self
83 }
84
85 #[must_use]
87 pub fn placement(mut self, value: EntityId) -> Self {
88 self.placement = Some(value);
89 self
90 }
91
92 #[must_use]
94 pub fn representation(mut self, value: EntityId) -> Self {
95 self.representation = Some(value);
96 self
97 }
98
99 #[must_use]
101 pub fn long_name(mut self, value: &'a str) -> Self {
102 self.long_name = Some(value);
103 self
104 }
105
106 #[must_use]
108 pub fn predefined_type(mut self, value: &'a str) -> Self {
109 self.predefined_type = Some(value);
110 self
111 }
112}
113
114pub fn create_external_spatial_element(
127 tx: &mut Transaction,
128 global_id: &str,
129 draft: ExternalSpatialDraft<'_>,
130) -> SpatialAuthoringResult<EntityId> {
131 check_external(global_id, &draft)?;
132 let mut attributes = vec![Value::Null; 9];
133 attributes[0] = Value::Text(global_id.into());
134 attributes[2] = optional_text(draft.name);
135 attributes[3] = optional_text(draft.description);
136 attributes[4] = optional_text(draft.object_type);
137 attributes[5] = draft.placement.map_or(Value::Null, Value::Ref);
138 attributes[6] = draft.representation.map_or(Value::Null, Value::Ref);
139 attributes[7] = optional_text(draft.long_name);
140 attributes[8] = draft
141 .predefined_type
142 .map_or(Value::Null, |t| Value::Enum(t.into()));
143 Ok(tx.create(Entity::new(EXTERNAL, attributes)))
144}
145
146pub(super) const EXTERNAL: &str = "IFCEXTERNALSPATIALELEMENT";
147
148pub(super) fn check_external(
150 global_id: &str,
151 draft: &ExternalSpatialDraft<'_>,
152) -> SpatialAuthoringResult<()> {
153 if Guid::parse(global_id).is_none() {
154 return Err(invalid(EXTERNAL, "GlobalId", global_id));
155 }
156 if let Some(token) = draft.predefined_type {
157 if !EXTERNAL_KIND.contains(&token) {
158 return Err(invalid(EXTERNAL, "PredefinedType", token));
159 }
160 if token == "USERDEFINED"
161 && draft
162 .object_type
163 .is_none_or(|value| value.trim().is_empty())
164 {
165 return Err(invalid(EXTERNAL, "ObjectType", "required by USERDEFINED"));
166 }
167 }
168 Ok(())
169}
170
171#[derive(Debug, Clone, Copy, Default)]
176#[non_exhaustive]
177pub struct ProjectLibraryDraft<'a> {
178 pub name: Option<&'a str>,
180 pub description: Option<&'a str>,
182 pub object_type: Option<&'a str>,
184 pub long_name: Option<&'a str>,
186 pub phase: Option<&'a str>,
188 pub units: Option<EntityId>,
190}
191
192impl<'a> ProjectLibraryDraft<'a> {
193 #[must_use]
195 pub fn new() -> Self {
196 Self::default()
197 }
198
199 #[must_use]
201 pub fn name(mut self, value: &'a str) -> Self {
202 self.name = Some(value);
203 self
204 }
205
206 #[must_use]
208 pub fn description(mut self, value: &'a str) -> Self {
209 self.description = Some(value);
210 self
211 }
212
213 #[must_use]
215 pub fn object_type(mut self, value: &'a str) -> Self {
216 self.object_type = Some(value);
217 self
218 }
219
220 #[must_use]
222 pub fn long_name(mut self, value: &'a str) -> Self {
223 self.long_name = Some(value);
224 self
225 }
226
227 #[must_use]
229 pub fn phase(mut self, value: &'a str) -> Self {
230 self.phase = Some(value);
231 self
232 }
233
234 #[must_use]
236 pub fn units(mut self, value: EntityId) -> Self {
237 self.units = Some(value);
238 self
239 }
240}
241
242pub fn create_project_library(
261 tx: &mut Transaction,
262 global_id: &str,
263 draft: ProjectLibraryDraft<'_>,
264 representation_contexts: &[EntityId],
265) -> SpatialAuthoringResult<EntityId> {
266 const ENTITY: &str = "IFCPROJECTLIBRARY";
267 if Guid::parse(global_id).is_none() {
268 return Err(invalid(ENTITY, "GlobalId", global_id));
269 }
270 let mut attributes = vec![Value::Null; 9];
271 attributes[0] = Value::Text(global_id.into());
272 attributes[2] = optional_text(draft.name);
273 attributes[3] = optional_text(draft.description);
274 attributes[4] = optional_text(draft.object_type);
275 attributes[5] = optional_text(draft.long_name);
276 attributes[6] = optional_text(draft.phase);
277 if !representation_contexts.is_empty() {
278 attributes[7] = Value::List(
279 representation_contexts
280 .iter()
281 .copied()
282 .map(Value::Ref)
283 .collect(),
284 );
285 }
286 attributes[8] = draft.units.map_or(Value::Null, Value::Ref);
287 Ok(tx.create(Entity::new(ENTITY, attributes)))
288}