ifc_resource/resource/
nesting.rs1use std::collections::{HashMap, HashSet};
4
5use ifc_model::{Budget, EntityId};
6
7use crate::error::{ResourceError, ResourceResult};
8use crate::view::ResourceView;
9
10struct NestingIndex {
11 children: HashMap<EntityId, Vec<EntityId>>,
12 parent: HashMap<EntityId, EntityId>,
13}
14
15impl<'m, 's> ResourceView<'m, 's> {
16 fn nesting_index(&self) -> ResourceResult<NestingIndex> {
17 let mut children: HashMap<EntityId, Vec<EntityId>> = HashMap::new();
18 let mut parent = HashMap::new();
19
20 for relation in self.ids_of_ancestor("IfcRelNests") {
21 let record = self.record(relation, "IfcRelNests")?;
22 let relating = record.required_ref_select(
23 "RelatingObject",
24 "IfcObjectDefinition",
25 &["IfcObjectDefinition"],
26 )?;
27 let relating_entity =
28 self.model
29 .get(relating)
30 .ok_or(ResourceError::DanglingReference {
31 entity: relation,
32 attribute: "RelatingObject",
33 target: relating,
34 })?;
35 if !self
36 .schema
37 .is_a(&relating_entity.type_name, "IfcConstructionResource")
38 {
39 continue;
40 }
41
42 let related =
43 record.refs("RelatedObjects", "IfcConstructionResource", 1, false, false)?;
44 for child in related {
45 if child == relating {
46 return Err(ResourceError::SemanticViolation {
47 entity: Some(relation),
48 rule: "IfcRelNests must not nest a resource under itself",
49 });
50 }
51 if let Some(existing) = parent.insert(child, relating) {
52 if existing == relating {
53 return Err(ResourceError::DuplicateReference {
54 entity: relation,
55 attribute: "RelatedObjects",
56 target: child,
57 });
58 }
59 return Err(ResourceError::SemanticViolation {
60 entity: Some(child),
61 rule: "IfcObject.Nests permits at most one resource parent",
62 });
63 }
64 children.entry(relating).or_default().push(child);
65 }
66 }
67 Ok(NestingIndex { children, parent })
68 }
69
70 pub fn parent_resource(&self, child: EntityId) -> ResourceResult<Option<EntityId>> {
72 self.resource(child)?;
73 Ok(self.nesting_index()?.parent.get(&child).copied())
74 }
75
76 pub fn direct_members(&self, parent: EntityId) -> ResourceResult<Vec<EntityId>> {
78 self.resource(parent)?;
79 Ok(self
80 .nesting_index()?
81 .children
82 .remove(&parent)
83 .unwrap_or_default())
84 }
85
86 pub fn descendants(&self, root: EntityId, budget: Budget) -> ResourceResult<Vec<EntityId>> {
88 self.resource(root)?;
89 if budget.max_nodes == 0 {
90 return Err(ResourceError::BudgetExceeded {
91 max_depth: budget.max_depth,
92 max_nodes: budget.max_nodes,
93 });
94 }
95
96 let index = self.nesting_index()?;
97 let mut result = Vec::new();
98 let mut visited = HashSet::from([root]);
99 let mut stack = vec![(root, 0_usize)];
100 while let Some((parent, depth)) = stack.pop() {
101 let children = index
102 .children
103 .get(&parent)
104 .map(Vec::as_slice)
105 .unwrap_or_default();
106 if !children.is_empty() && depth >= budget.max_depth {
107 return Err(ResourceError::BudgetExceeded {
108 max_depth: budget.max_depth,
109 max_nodes: budget.max_nodes,
110 });
111 }
112 for child in children {
113 if !visited.insert(*child) {
114 return Err(ResourceError::Cycle { at: *child });
115 }
116 if visited.len() > budget.max_nodes {
117 return Err(ResourceError::BudgetExceeded {
118 max_depth: budget.max_depth,
119 max_nodes: budget.max_nodes,
120 });
121 }
122 result.push(*child);
123 }
124 stack.extend(children.iter().rev().map(|child| (*child, depth + 1)));
125 }
126 Ok(result)
127 }
128}