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    /// Get all entity IDs in the model
114    ///
115    /// # Returns
116    /// A vector of all entity IDs
117    fn all_ids(&self) -> Vec<EntityId>;
118
119    /// Get total entity count
120    fn entity_count(&self) -> usize {
121        self.all_ids().len()
122    }
123
124    /// Fast raw bytes access for optimized parsing
125    ///
126    /// Returns the raw bytes of an entity's definition for parsers that
127    /// want to do direct parsing without going through the attribute system.
128    /// This is useful for performance-critical paths like coordinate parsing.
129    ///
130    /// # Arguments
131    /// * `id` - The entity ID to get raw bytes for
132    ///
133    /// # Returns
134    /// The raw bytes of the entity definition, or `None` if not available
135    fn raw_bytes(&self, id: EntityId) -> Option<&[u8]>;
136}
137
138/// Extension methods for EntityResolver
139pub trait EntityResolverExt: EntityResolver {
140    /// Get entity by raw u32 ID
141    fn get_by_u32(&self, id: u32) -> Option<Arc<DecodedEntity>> {
142        self.get(EntityId(id))
143    }
144
145    /// Check if an entity exists
146    fn exists(&self, id: EntityId) -> bool {
147        self.get(id).is_some()
148    }
149
150    /// Get entity or return error
151    fn get_or_err(&self, id: EntityId) -> crate::Result<Arc<DecodedEntity>> {
152        self.get(id).ok_or(crate::ParseError::EntityNotFound(id))
153    }
154
155    /// Resolve reference or return error
156    fn resolve_ref_or_err(
157        &self,
158        entity_id: EntityId,
159        attr_index: usize,
160        attr: &AttributeValue,
161    ) -> crate::Result<Arc<DecodedEntity>> {
162        self.resolve_ref(attr)
163            .ok_or(crate::ParseError::InvalidReference {
164                entity: entity_id,
165                attribute: attr_index,
166            })
167    }
168}
169
170// Blanket implementation for all EntityResolver types
171impl<T: EntityResolver + ?Sized> EntityResolverExt for T {}