Skip to main content

bimifc_model/
resolver.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! Entity resolution trait for looking up and resolving IFC entities
6
7use crate::{AttributeValue, DecodedEntity, EntityId, IfcType};
8use std::sync::Arc;
9
10/// Entity lookup and reference resolution
11///
12/// This trait provides the core functionality for accessing IFC entities
13/// and resolving entity references. Implementations should provide O(1)
14/// lookup by entity ID.
15///
16/// # Example
17///
18/// ```ignore
19/// use bimifc_model::{EntityResolver, EntityId, AttributeValue};
20///
21/// fn process_wall(resolver: &dyn EntityResolver, wall_id: EntityId) {
22///     if let Some(wall) = resolver.get(wall_id) {
23///         println!("Wall type: {:?}", wall.ifc_type);
24///
25///         // Resolve a reference attribute
26///         if let Some(ref_attr) = wall.get(5) {
27///             if let Some(related) = resolver.resolve_ref(ref_attr) {
28///                 println!("Related entity: {:?}", related.ifc_type);
29///             }
30///         }
31///     }
32/// }
33/// ```
34pub trait EntityResolver: Send + Sync {
35    /// Get entity by ID
36    ///
37    /// Returns the decoded entity if it exists, wrapped in an Arc for
38    /// efficient sharing.
39    ///
40    /// # Arguments
41    /// * `id` - The entity ID to look up
42    ///
43    /// # Returns
44    /// `Some(Arc<DecodedEntity>)` if found, `None` otherwise
45    fn get(&self, id: EntityId) -> Option<Arc<DecodedEntity>>;
46
47    /// Resolve an entity reference from an attribute value
48    ///
49    /// If the attribute value is an EntityRef, this looks up and returns
50    /// the referenced entity.
51    ///
52    /// # Arguments
53    /// * `attr` - The attribute value that may contain an entity reference
54    ///
55    /// # Returns
56    /// `Some(Arc<DecodedEntity>)` if the attribute is a valid reference, `None` otherwise
57    fn resolve_ref(&self, attr: &AttributeValue) -> Option<Arc<DecodedEntity>> {
58        match attr {
59            AttributeValue::EntityRef(id) => self.get(*id),
60            _ => None,
61        }
62    }
63
64    /// Resolve a list of entity references
65    ///
66    /// If the attribute value is a List containing EntityRefs, this resolves
67    /// all of them and returns the entities.
68    ///
69    /// # Arguments
70    /// * `attr` - The attribute value that may contain a list of entity references
71    ///
72    /// # Returns
73    /// A vector of resolved entities (empty if the attribute is not a list or contains no refs)
74    fn resolve_ref_list(&self, attr: &AttributeValue) -> Vec<Arc<DecodedEntity>> {
75        match attr {
76            AttributeValue::List(items) => items
77                .iter()
78                .filter_map(|item| self.resolve_ref(item))
79                .collect(),
80            _ => Vec::new(),
81        }
82    }
83
84    /// Get all entities of a specific type
85    ///
86    /// # Arguments
87    /// * `ifc_type` - The IFC type to filter by
88    ///
89    /// # Returns
90    /// A vector of all entities matching the specified type
91    fn entities_by_type(&self, ifc_type: &IfcType) -> Vec<Arc<DecodedEntity>>;
92
93    /// Find entities by type name string
94    ///
95    /// This is useful for dynamic lookups where the type is not known at compile time.
96    ///
97    /// # Arguments
98    /// * `type_name` - The type name to search for (case-insensitive)
99    ///
100    /// # Returns
101    /// A vector of entities matching the type name
102    fn find_by_type_name(&self, type_name: &str) -> Vec<Arc<DecodedEntity>>;
103
104    /// Count entities of a specific type
105    ///
106    /// # Arguments
107    /// * `ifc_type` - The IFC type to count
108    ///
109    /// # Returns
110    /// The number of entities of the specified type
111    fn count_by_type(&self, ifc_type: &IfcType) -> usize;
112
113    /// All `IfcType` keys present in the model's type index.
114    ///
115    /// Used by callers that want to iterate entities by type without
116    /// hardcoding a known-types list. Default impl walks all entity IDs
117    /// and dedupes; implementations with a real type index should
118    /// override it for speed.
119    fn types_present(&self) -> Vec<IfcType> {
120        use std::collections::HashSet;
121        let mut seen: HashSet<String> = HashSet::new();
122        let mut out = Vec::new();
123        for id in self.all_ids() {
124            if let Some(e) = self.get(id) {
125                let key = format!("{:?}", e.ifc_type);
126                if seen.insert(key) {
127                    out.push(e.ifc_type.clone());
128                }
129            }
130        }
131        out
132    }
133
134    /// Get all entity IDs in the model
135    ///
136    /// # Returns
137    /// A vector of all entity IDs
138    fn all_ids(&self) -> Vec<EntityId>;
139
140    /// Get total entity count
141    fn entity_count(&self) -> usize {
142        self.all_ids().len()
143    }
144
145    /// Fast raw bytes access for optimized parsing
146    ///
147    /// Returns the raw bytes of an entity's definition for parsers that
148    /// want to do direct parsing without going through the attribute system.
149    /// This is useful for performance-critical paths like coordinate parsing.
150    ///
151    /// # Arguments
152    /// * `id` - The entity ID to get raw bytes for
153    ///
154    /// # Returns
155    /// The raw bytes of the entity definition, or `None` if not available
156    fn raw_bytes(&self, id: EntityId) -> Option<&[u8]>;
157}
158
159/// Extension methods for EntityResolver
160pub trait EntityResolverExt: EntityResolver {
161    /// Get entity by raw u32 ID
162    fn get_by_u32(&self, id: u32) -> Option<Arc<DecodedEntity>> {
163        self.get(EntityId(id))
164    }
165
166    /// Check if an entity exists
167    fn exists(&self, id: EntityId) -> bool {
168        self.get(id).is_some()
169    }
170
171    /// Get entity or return error
172    fn get_or_err(&self, id: EntityId) -> crate::Result<Arc<DecodedEntity>> {
173        self.get(id).ok_or(crate::ParseError::EntityNotFound(id))
174    }
175
176    /// Resolve reference or return error
177    fn resolve_ref_or_err(
178        &self,
179        entity_id: EntityId,
180        attr_index: usize,
181        attr: &AttributeValue,
182    ) -> crate::Result<Arc<DecodedEntity>> {
183        self.resolve_ref(attr)
184            .ok_or(crate::ParseError::InvalidReference {
185                entity: entity_id,
186                attribute: attr_index,
187            })
188    }
189}
190
191// Blanket implementation for all EntityResolver types
192impl<T: EntityResolver + ?Sized> EntityResolverExt for T {}