Skip to main content

polyscope_core/
registry.rs

1//! Structure registry for managing registered structures.
2
3use std::collections::HashMap;
4
5use crate::error::{PolyscopeError, Result};
6use crate::structure::Structure;
7
8/// Registry for managing all structures in polyscope.
9///
10/// Structures are organized by type name and then by instance name.
11#[derive(Default)]
12pub struct Registry {
13    /// Map from type name -> (instance name -> structure)
14    structures: HashMap<String, HashMap<String, Box<dyn Structure>>>,
15}
16
17impl Registry {
18    /// Creates a new empty registry.
19    #[must_use]
20    pub fn new() -> Self {
21        Self::default()
22    }
23
24    /// Registers a structure with the registry.
25    ///
26    /// Returns an error if a structure with the same type and name already exists.
27    pub fn register(&mut self, structure: Box<dyn Structure>) -> Result<()> {
28        let type_name = structure.type_name().to_string();
29        let name = structure.name().to_string();
30
31        let type_map = self.structures.entry(type_name).or_default();
32
33        if type_map.contains_key(&name) {
34            return Err(PolyscopeError::StructureExists(name));
35        }
36
37        type_map.insert(name, structure);
38        Ok(())
39    }
40
41    /// Gets a reference to a structure by type and name.
42    #[must_use]
43    pub fn get(&self, type_name: &str, name: &str) -> Option<&dyn Structure> {
44        self.structures
45            .get(type_name)
46            .and_then(|m| m.get(name))
47            .map(std::convert::AsRef::as_ref)
48    }
49
50    /// Gets a mutable reference to a structure by type and name.
51    pub fn get_mut(&mut self, type_name: &str, name: &str) -> Option<&mut Box<dyn Structure>> {
52        self.structures.get_mut(type_name)?.get_mut(name)
53    }
54
55    /// Checks if a structure with the given type and name exists.
56    #[must_use]
57    pub fn contains(&self, type_name: &str, name: &str) -> bool {
58        self.structures
59            .get(type_name)
60            .is_some_and(|m| m.contains_key(name))
61    }
62
63    /// Removes a structure by type and name.
64    pub fn remove(&mut self, type_name: &str, name: &str) -> Option<Box<dyn Structure>> {
65        self.structures
66            .get_mut(type_name)
67            .and_then(|m| m.remove(name))
68    }
69
70    /// Removes all structures of a given type.
71    pub fn remove_all_of_type(&mut self, type_name: &str) {
72        self.structures.remove(type_name);
73    }
74
75    /// Removes all structures from the registry.
76    pub fn clear(&mut self) {
77        self.structures.clear();
78    }
79
80    /// Returns an iterator over all structures.
81    pub fn iter(&self) -> impl Iterator<Item = &dyn Structure> {
82        self.structures
83            .values()
84            .flat_map(|m| m.values())
85            .map(std::convert::AsRef::as_ref)
86    }
87
88    /// Returns a mutable iterator over all structures.
89    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Box<dyn Structure>> + '_ {
90        self.structures.values_mut().flat_map(|m| m.values_mut())
91    }
92
93    /// Returns the total number of registered structures.
94    #[must_use]
95    pub fn len(&self) -> usize {
96        self.structures
97            .values()
98            .map(std::collections::HashMap::len)
99            .sum()
100    }
101
102    /// Returns true if the registry is empty.
103    #[must_use]
104    pub fn is_empty(&self) -> bool {
105        self.structures
106            .values()
107            .all(std::collections::HashMap::is_empty)
108    }
109
110    /// Returns all structures of a given type.
111    pub fn get_all_of_type(&self, type_name: &str) -> impl Iterator<Item = &dyn Structure> {
112        self.structures
113            .get(type_name)
114            .into_iter()
115            .flat_map(|m| m.values())
116            .map(std::convert::AsRef::as_ref)
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use std::any::Any;
123
124    use super::*;
125    use crate::pick::PickResult;
126    use glam::{Mat4, Vec3};
127
128    /// Minimal mock structure for testing the registry.
129    struct MockStructure {
130        name: String,
131        type_name: &'static str,
132        enabled: bool,
133        transform: Mat4,
134    }
135
136    impl MockStructure {
137        fn new(name: &str, type_name: &'static str) -> Self {
138            Self {
139                name: name.to_string(),
140                type_name,
141                enabled: true,
142                transform: Mat4::IDENTITY,
143            }
144        }
145    }
146
147    impl Structure for MockStructure {
148        fn as_any(&self) -> &dyn Any {
149            self
150        }
151        fn as_any_mut(&mut self) -> &mut dyn Any {
152            self
153        }
154        fn name(&self) -> &str {
155            &self.name
156        }
157        fn type_name(&self) -> &'static str {
158            self.type_name
159        }
160        fn bounding_box(&self) -> Option<(Vec3, Vec3)> {
161            None
162        }
163        fn length_scale(&self) -> f32 {
164            1.0
165        }
166        fn transform(&self) -> Mat4 {
167            self.transform
168        }
169        fn set_transform(&mut self, transform: Mat4) {
170            self.transform = transform;
171        }
172        fn is_enabled(&self) -> bool {
173            self.enabled
174        }
175        fn set_enabled(&mut self, enabled: bool) {
176            self.enabled = enabled;
177        }
178        fn draw(&self, _ctx: &mut dyn crate::structure::RenderContext) {}
179        fn draw_pick(&self, _ctx: &mut dyn crate::structure::RenderContext) {}
180        fn build_ui(&mut self, _ui: &dyn Any) {}
181        fn build_pick_ui(&self, _ui: &dyn Any, _pick: &PickResult) {}
182        fn refresh(&mut self) {}
183    }
184
185    fn mock(name: &str, type_name: &'static str) -> Box<dyn Structure> {
186        Box::new(MockStructure::new(name, type_name))
187    }
188
189    #[test]
190    fn test_new_registry_is_empty() {
191        let reg = Registry::new();
192        assert!(reg.is_empty());
193        assert_eq!(reg.len(), 0);
194    }
195
196    #[test]
197    fn test_register_and_get() {
198        let mut reg = Registry::new();
199        reg.register(mock("bunny", "SurfaceMesh")).unwrap();
200
201        assert!(!reg.is_empty());
202        assert_eq!(reg.len(), 1);
203        assert!(reg.contains("SurfaceMesh", "bunny"));
204
205        let s = reg.get("SurfaceMesh", "bunny").unwrap();
206        assert_eq!(s.name(), "bunny");
207        assert_eq!(s.type_name(), "SurfaceMesh");
208    }
209
210    #[test]
211    fn test_register_duplicate_errors() {
212        let mut reg = Registry::new();
213        reg.register(mock("bunny", "SurfaceMesh")).unwrap();
214
215        let result = reg.register(mock("bunny", "SurfaceMesh"));
216        assert!(result.is_err());
217    }
218
219    #[test]
220    fn test_same_name_different_types() {
221        let mut reg = Registry::new();
222        reg.register(mock("data", "SurfaceMesh")).unwrap();
223        reg.register(mock("data", "PointCloud")).unwrap();
224
225        assert_eq!(reg.len(), 2);
226        assert!(reg.contains("SurfaceMesh", "data"));
227        assert!(reg.contains("PointCloud", "data"));
228    }
229
230    #[test]
231    fn test_get_nonexistent() {
232        let reg = Registry::new();
233        assert!(reg.get("SurfaceMesh", "bunny").is_none());
234        assert!(!reg.contains("SurfaceMesh", "bunny"));
235    }
236
237    #[test]
238    fn test_get_mut() {
239        let mut reg = Registry::new();
240        reg.register(mock("bunny", "SurfaceMesh")).unwrap();
241
242        let s = reg.get_mut("SurfaceMesh", "bunny").unwrap();
243        s.set_enabled(false);
244
245        let s = reg.get("SurfaceMesh", "bunny").unwrap();
246        assert!(!s.is_enabled());
247    }
248
249    #[test]
250    fn test_remove() {
251        let mut reg = Registry::new();
252        reg.register(mock("bunny", "SurfaceMesh")).unwrap();
253
254        let removed = reg.remove("SurfaceMesh", "bunny");
255        assert!(removed.is_some());
256        assert_eq!(removed.unwrap().name(), "bunny");
257        assert!(reg.get("SurfaceMesh", "bunny").is_none());
258    }
259
260    #[test]
261    fn test_remove_nonexistent() {
262        let mut reg = Registry::new();
263        assert!(reg.remove("SurfaceMesh", "bunny").is_none());
264    }
265
266    #[test]
267    fn test_remove_all_of_type() {
268        let mut reg = Registry::new();
269        reg.register(mock("a", "SurfaceMesh")).unwrap();
270        reg.register(mock("b", "SurfaceMesh")).unwrap();
271        reg.register(mock("c", "PointCloud")).unwrap();
272
273        reg.remove_all_of_type("SurfaceMesh");
274        assert_eq!(reg.len(), 1);
275        assert!(!reg.contains("SurfaceMesh", "a"));
276        assert!(reg.contains("PointCloud", "c"));
277    }
278
279    #[test]
280    fn test_clear() {
281        let mut reg = Registry::new();
282        reg.register(mock("a", "SurfaceMesh")).unwrap();
283        reg.register(mock("b", "PointCloud")).unwrap();
284
285        reg.clear();
286        assert!(reg.is_empty());
287        assert_eq!(reg.len(), 0);
288    }
289
290    #[test]
291    fn test_iter() {
292        let mut reg = Registry::new();
293        reg.register(mock("a", "SurfaceMesh")).unwrap();
294        reg.register(mock("b", "PointCloud")).unwrap();
295        reg.register(mock("c", "SurfaceMesh")).unwrap();
296
297        let names: Vec<&str> = reg
298            .iter()
299            .map(super::super::structure::Structure::name)
300            .collect();
301        assert_eq!(names.len(), 3);
302        assert!(names.contains(&"a"));
303        assert!(names.contains(&"b"));
304        assert!(names.contains(&"c"));
305    }
306
307    #[test]
308    fn test_get_all_of_type() {
309        let mut reg = Registry::new();
310        reg.register(mock("a", "SurfaceMesh")).unwrap();
311        reg.register(mock("b", "SurfaceMesh")).unwrap();
312        reg.register(mock("c", "PointCloud")).unwrap();
313
314        let meshes: Vec<&str> = reg
315            .get_all_of_type("SurfaceMesh")
316            .map(|s| s.name())
317            .collect();
318        assert_eq!(meshes.len(), 2);
319        assert!(meshes.contains(&"a"));
320        assert!(meshes.contains(&"b"));
321    }
322
323    #[test]
324    fn test_get_all_of_type_empty() {
325        let reg = Registry::new();
326        assert_eq!(reg.get_all_of_type("SurfaceMesh").count(), 0);
327    }
328}