1use ifc_model::guid::Guid;
4use ifc_model::{Entity, EntityId};
5
6use crate::view::{
7 optional_ref, optional_text, required_ref, required_refs, required_text, validate_target,
8 wrong, ApprovalView,
9};
10use crate::{ApprovalError, ApprovalResult};
11
12const APPROVAL: &str = "IFCAPPROVAL";
13const APPROVAL_REL: &str = "IFCAPPROVALRELATIONSHIP";
14const RESOURCE_REL: &str = "IFCRESOURCEAPPROVALRELATIONSHIP";
15const ASSIGNMENT: &str = "IFCRELASSOCIATESAPPROVAL";
16
17macro_rules! projection {
18 ($name:ident, $kind:expr) => {
19 #[derive(Debug, Clone, Copy)]
21 pub struct $name<'m> {
22 id: EntityId,
23 entity: &'m Entity,
24 }
25 impl<'m> $name<'m> {
26 pub fn try_new(id: EntityId, entity: &'m Entity) -> ApprovalResult<Self> {
28 if entity.is_type($kind) {
29 Ok(Self { id, entity })
30 } else {
31 Err(wrong($kind, entity))
32 }
33 }
34 #[must_use]
36 pub const fn id(self) -> EntityId {
37 self.id
38 }
39 }
40 };
41}
42
43projection!(Approval, APPROVAL);
44projection!(ApprovalRelationship, APPROVAL_REL);
45projection!(ResourceApprovalRelationship, RESOURCE_REL);
46projection!(ApprovalAssignment, ASSIGNMENT);
47
48impl<'m> Approval<'m> {
49 pub fn identifier(self) -> ApprovalResult<Option<&'m str>> {
51 optional_text(APPROVAL, self.id, self.entity, 0, "Identifier")
52 }
53 pub fn name(self) -> ApprovalResult<Option<&'m str>> {
55 optional_text(APPROVAL, self.id, self.entity, 1, "Name")
56 }
57 pub fn description(self) -> ApprovalResult<Option<&'m str>> {
59 optional_text(APPROVAL, self.id, self.entity, 2, "Description")
60 }
61 pub fn time_of_approval(self) -> ApprovalResult<Option<&'m str>> {
63 optional_text(APPROVAL, self.id, self.entity, 3, "TimeOfApproval")
64 }
65 pub fn status(self) -> ApprovalResult<Option<&'m str>> {
67 optional_text(APPROVAL, self.id, self.entity, 4, "Status")
68 }
69 pub fn level(self) -> ApprovalResult<Option<&'m str>> {
71 optional_text(APPROVAL, self.id, self.entity, 5, "Level")
72 }
73 pub fn qualifier(self) -> ApprovalResult<Option<&'m str>> {
75 optional_text(APPROVAL, self.id, self.entity, 6, "Qualifier")
76 }
77 pub fn requesting_approval(self) -> ApprovalResult<Option<EntityId>> {
79 optional_ref(APPROVAL, self.id, self.entity, 7, "RequestingApproval")
80 }
81 pub fn giving_approval(self) -> ApprovalResult<Option<EntityId>> {
83 optional_ref(APPROVAL, self.id, self.entity, 8, "GivingApproval")
84 }
85
86 fn validate(self, view: ApprovalView<'m>) -> ApprovalResult<Self> {
87 if self.identifier()?.is_none() && self.name()?.is_none() {
88 return Err(ApprovalError::Semantic {
89 entity: APPROVAL,
90 id: self.id,
91 rule: "HasIdentifierOrName",
92 detail: "Identifier and Name are both absent".into(),
93 });
94 }
95 for (attribute, target) in [
96 ("RequestingApproval", self.requesting_approval()?),
97 ("GivingApproval", self.giving_approval()?),
98 ] {
99 if let Some(target) = target {
100 validate_target(
101 view.model(),
102 APPROVAL,
103 self.id,
104 attribute,
105 target,
106 "IfcActorSelect",
107 )?;
108 }
109 }
110 Ok(self)
111 }
112}
113
114impl<'m> ApprovalRelationship<'m> {
115 pub fn name(self) -> ApprovalResult<Option<&'m str>> {
117 optional_text(APPROVAL_REL, self.id, self.entity, 0, "Name")
118 }
119 pub fn description(self) -> ApprovalResult<Option<&'m str>> {
121 optional_text(APPROVAL_REL, self.id, self.entity, 1, "Description")
122 }
123 pub fn relating_approval(self) -> ApprovalResult<EntityId> {
125 required_ref(APPROVAL_REL, self.id, self.entity, 2, "RelatingApproval")
126 }
127 pub fn related_approvals(self) -> ApprovalResult<Vec<EntityId>> {
129 required_refs(APPROVAL_REL, self.id, self.entity, 3, "RelatedApprovals")
130 }
131 fn validate(self, view: ApprovalView<'m>) -> ApprovalResult<Self> {
132 let relating = self.relating_approval()?;
133 validate_target(
134 view.model(),
135 APPROVAL_REL,
136 self.id,
137 "RelatingApproval",
138 relating,
139 APPROVAL,
140 )?;
141 for target in self.related_approvals()? {
142 if target == relating {
143 return Err(ApprovalError::Semantic {
144 entity: APPROVAL_REL,
145 id: self.id,
146 rule: "NoSelfRelationship",
147 detail: format!("approval {target} appears on both ends"),
148 });
149 }
150 validate_target(
151 view.model(),
152 APPROVAL_REL,
153 self.id,
154 "RelatedApprovals",
155 target,
156 APPROVAL,
157 )?;
158 }
159 Ok(self)
160 }
161}
162
163impl<'m> ResourceApprovalRelationship<'m> {
164 pub fn name(self) -> ApprovalResult<Option<&'m str>> {
166 optional_text(RESOURCE_REL, self.id, self.entity, 0, "Name")
167 }
168 pub fn description(self) -> ApprovalResult<Option<&'m str>> {
170 optional_text(RESOURCE_REL, self.id, self.entity, 1, "Description")
171 }
172 pub fn related_resources(self) -> ApprovalResult<Vec<EntityId>> {
174 required_refs(
175 RESOURCE_REL,
176 self.id,
177 self.entity,
178 2,
179 "RelatedResourceObjects",
180 )
181 }
182 pub fn relating_approval(self) -> ApprovalResult<EntityId> {
184 required_ref(RESOURCE_REL, self.id, self.entity, 3, "RelatingApproval")
185 }
186 fn validate(self, view: ApprovalView<'m>) -> ApprovalResult<Self> {
187 validate_target(
188 view.model(),
189 RESOURCE_REL,
190 self.id,
191 "RelatingApproval",
192 self.relating_approval()?,
193 APPROVAL,
194 )?;
195 for target in self.related_resources()? {
196 validate_target(
197 view.model(),
198 RESOURCE_REL,
199 self.id,
200 "RelatedResourceObjects",
201 target,
202 "IfcResourceObjectSelect",
203 )?;
204 }
205 Ok(self)
206 }
207}
208
209impl<'m> ApprovalAssignment<'m> {
210 pub fn global_id(self) -> ApprovalResult<&'m str> {
212 required_text(ASSIGNMENT, self.id, self.entity, 0, "GlobalId")
213 }
214 pub fn name(self) -> ApprovalResult<Option<&'m str>> {
216 optional_text(ASSIGNMENT, self.id, self.entity, 2, "Name")
217 }
218 pub fn description(self) -> ApprovalResult<Option<&'m str>> {
220 optional_text(ASSIGNMENT, self.id, self.entity, 3, "Description")
221 }
222 pub fn related_objects(self) -> ApprovalResult<Vec<EntityId>> {
224 required_refs(ASSIGNMENT, self.id, self.entity, 4, "RelatedObjects")
225 }
226 pub fn relating_approval(self) -> ApprovalResult<EntityId> {
228 required_ref(ASSIGNMENT, self.id, self.entity, 5, "RelatingApproval")
229 }
230 fn validate(self, view: ApprovalView<'m>) -> ApprovalResult<Self> {
231 if Guid::parse(self.global_id()?).is_none() {
232 return Err(ApprovalError::InvalidValue {
233 entity: ASSIGNMENT,
234 id: self.id,
235 attribute: "GlobalId",
236 value: self.global_id()?.into(),
237 });
238 }
239 validate_target(
240 view.model(),
241 ASSIGNMENT,
242 self.id,
243 "RelatingApproval",
244 self.relating_approval()?,
245 APPROVAL,
246 )?;
247 for target in self.related_objects()? {
248 validate_target(
249 view.model(),
250 ASSIGNMENT,
251 self.id,
252 "RelatedObjects",
253 target,
254 "IfcDefinitionSelect",
255 )?;
256 }
257 Ok(self)
258 }
259}
260
261impl<'m> ApprovalView<'m> {
262 pub fn approval(self, id: EntityId) -> ApprovalResult<Approval<'m>> {
264 let entity = self
265 .model()
266 .get(id)
267 .ok_or(ApprovalError::UnknownEntity { id })?;
268 Approval::try_new(id, entity)?.validate(self)
269 }
270 pub fn approval_relationship(self, id: EntityId) -> ApprovalResult<ApprovalRelationship<'m>> {
272 let entity = self
273 .model()
274 .get(id)
275 .ok_or(ApprovalError::UnknownEntity { id })?;
276 ApprovalRelationship::try_new(id, entity)?.validate(self)
277 }
278 pub fn resource_approval_relationship(
280 self,
281 id: EntityId,
282 ) -> ApprovalResult<ResourceApprovalRelationship<'m>> {
283 let entity = self
284 .model()
285 .get(id)
286 .ok_or(ApprovalError::UnknownEntity { id })?;
287 ResourceApprovalRelationship::try_new(id, entity)?.validate(self)
288 }
289 pub fn approval_assignment(self, id: EntityId) -> ApprovalResult<ApprovalAssignment<'m>> {
291 let entity = self
292 .model()
293 .get(id)
294 .ok_or(ApprovalError::UnknownEntity { id })?;
295 ApprovalAssignment::try_new(id, entity)?.validate(self)
296 }
297
298 pub fn resources_approved_by(self, approval: EntityId) -> ApprovalResult<Vec<EntityId>> {
300 self.approval(approval)?;
301 let mut out = Vec::new();
302 for (id, entity) in self.model().of_type(RESOURCE_REL) {
303 let relationship = ResourceApprovalRelationship { id, entity };
304 if relationship.relating_approval()? == approval {
305 out.extend(relationship.validate(self)?.related_resources()?);
306 }
307 }
308 Ok(out)
309 }
310
311 pub fn objects_approved_by(self, approval: EntityId) -> ApprovalResult<Vec<EntityId>> {
313 self.approval(approval)?;
314 let mut out = Vec::new();
315 for (id, entity) in self.model().of_type(ASSIGNMENT) {
316 let assignment = ApprovalAssignment { id, entity };
317 if assignment.relating_approval()? == approval {
318 out.extend(assignment.validate(self)?.related_objects()?);
319 }
320 }
321 Ok(out)
322 }
323}