Skip to main content

scarab_plugin_api/object_model/
registry.rs

1//! Registry trait and types for managing object lifecycle
2//!
3//! The registry provides centralized management of object handles and their
4//! associated data. Implementations can be in the daemon (for PTY state)
5//! or client (for UI state).
6
7use crate::object_model::{ObjectError, ObjectHandle};
8
9/// Entry in the object registry containing the object and its handle
10///
11/// This struct wraps an object with its associated handle, providing
12/// a unified way to store and retrieve objects from the registry.
13///
14/// # Type Parameters
15///
16/// * `T` - The type of object being stored (e.g., Window, Tab, Pane)
17#[derive(Debug, Clone)]
18pub struct RegistryEntry<T> {
19    /// The handle for this object
20    pub handle: ObjectHandle,
21    /// The actual object data
22    pub object: T,
23}
24
25impl<T> RegistryEntry<T> {
26    /// Create a new registry entry
27    ///
28    /// # Arguments
29    ///
30    /// * `handle` - The handle for this object
31    /// * `object` - The actual object data
32    pub fn new(handle: ObjectHandle, object: T) -> Self {
33        Self { handle, object }
34    }
35
36    /// Get the object's handle
37    #[inline]
38    pub fn handle(&self) -> ObjectHandle {
39        self.handle
40    }
41
42    /// Get a reference to the object
43    #[inline]
44    pub fn object(&self) -> &T {
45        &self.object
46    }
47
48    /// Get a mutable reference to the object
49    #[inline]
50    pub fn object_mut(&mut self) -> &mut T {
51        &mut self.object
52    }
53
54    /// Consume the entry and return the object
55    #[inline]
56    pub fn into_object(self) -> T {
57        self.object
58    }
59
60    /// Check if this entry's handle is still valid
61    #[inline]
62    pub fn is_valid(&self, current_generation: u32) -> bool {
63        self.handle.is_valid(current_generation)
64    }
65}
66
67/// Trait for managing object lifecycle in a registry
68///
69/// Implementations of this trait provide storage and retrieval of objects
70/// by their handles, with support for generation-based invalidation.
71///
72/// # Type Parameters
73///
74/// * `T` - The type of object being managed
75///
76/// # Examples
77///
78/// ```ignore
79/// // Example implementation (not compiled in doctests)
80/// struct MyRegistry {
81///     objects: HashMap<u64, RegistryEntry<MyObject>>,
82///     next_id: u64,
83///     generations: HashMap<u64, u32>,
84/// }
85///
86/// impl ObjectRegistry<MyObject> for MyRegistry {
87///     fn register(&mut self, object: MyObject) -> ObjectHandle {
88///         let id = self.next_id();
89///         let generation = self.generations.get(&id).copied().unwrap_or(0);
90///         let handle = ObjectHandle::new(ObjectType::Window, id, generation);
91///         self.objects.insert(id, RegistryEntry::new(handle, object));
92///         handle
93///     }
94///     // ... other methods
95/// }
96/// ```
97pub trait ObjectRegistry<T> {
98    /// Register a new object and return its handle
99    ///
100    /// This allocates a new ID for the object and returns a handle that can
101    /// be used to reference it later.
102    ///
103    /// # Arguments
104    ///
105    /// * `object` - The object to register
106    ///
107    /// # Returns
108    ///
109    /// A handle that can be used to access the object
110    fn register(&mut self, object: T) -> ObjectHandle;
111
112    /// Unregister an object by its handle
113    ///
114    /// This removes the object from the registry and increments the generation
115    /// counter for its ID, invalidating any existing handles.
116    ///
117    /// # Arguments
118    ///
119    /// * `handle` - The handle of the object to unregister
120    ///
121    /// # Returns
122    ///
123    /// * `Ok(object)` - The removed object if successful
124    /// * `Err(ObjectError)` - If the handle is invalid or not found
125    fn unregister(&mut self, handle: ObjectHandle) -> Result<T, ObjectError>;
126
127    /// Get a reference to an object by its handle
128    ///
129    /// # Arguments
130    ///
131    /// * `handle` - The handle of the object to retrieve
132    ///
133    /// # Returns
134    ///
135    /// * `Ok(&T)` - A reference to the object if found and valid
136    /// * `Err(ObjectError)` - If the handle is invalid or not found
137    fn get(&self, handle: ObjectHandle) -> Result<&T, ObjectError>;
138
139    /// Get a mutable reference to an object by its handle
140    ///
141    /// # Arguments
142    ///
143    /// * `handle` - The handle of the object to retrieve
144    ///
145    /// # Returns
146    ///
147    /// * `Ok(&mut T)` - A mutable reference to the object if found and valid
148    /// * `Err(ObjectError)` - If the handle is invalid or not found
149    fn get_mut(&mut self, handle: ObjectHandle) -> Result<&mut T, ObjectError>;
150
151    /// Get an object by its ID, regardless of generation
152    ///
153    /// This is useful for debugging or administrative operations where you
154    /// need to access an object even if the handle might be stale.
155    ///
156    /// # Arguments
157    ///
158    /// * `id` - The object ID
159    ///
160    /// # Returns
161    ///
162    /// * `Some(&T)` - A reference to the object if found
163    /// * `None` - If no object with this ID exists
164    fn get_by_id(&self, id: u64) -> Option<&T>;
165
166    /// Get the next available object ID
167    ///
168    /// This should return a unique ID that hasn't been used yet, or has been
169    /// recycled after unregistration.
170    ///
171    /// # Returns
172    ///
173    /// A unique object ID
174    fn next_id(&mut self) -> u64;
175
176    /// Increment the generation counter for an object ID
177    ///
178    /// This is called when an object is unregistered to invalidate existing
179    /// handles. The next object to use this ID will have a higher generation.
180    ///
181    /// # Arguments
182    ///
183    /// * `id` - The object ID whose generation should be incremented
184    fn increment_generation(&mut self, id: u64);
185
186    /// Get the current generation for an object ID
187    ///
188    /// # Arguments
189    ///
190    /// * `id` - The object ID
191    ///
192    /// # Returns
193    ///
194    /// The current generation counter for this ID
195    fn current_generation(&self, id: u64) -> u32;
196
197    /// Check if a handle is still valid
198    ///
199    /// # Arguments
200    ///
201    /// * `handle` - The handle to validate
202    ///
203    /// # Returns
204    ///
205    /// `true` if the handle references a valid object with the correct generation
206    fn is_valid(&self, handle: ObjectHandle) -> bool {
207        let current_gen = self.current_generation(handle.id());
208        handle.is_valid(current_gen) && self.get_by_id(handle.id()).is_some()
209    }
210
211    /// Get all registered object IDs
212    ///
213    /// # Returns
214    ///
215    /// A vector of all currently registered object IDs
216    fn all_ids(&self) -> Vec<u64>;
217
218    /// Get the number of registered objects
219    ///
220    /// # Returns
221    ///
222    /// The count of objects currently in the registry
223    fn len(&self) -> usize;
224
225    /// Check if the registry is empty
226    ///
227    /// # Returns
228    ///
229    /// `true` if no objects are registered
230    fn is_empty(&self) -> bool {
231        self.len() == 0
232    }
233
234    /// Get the parent handle of an object
235    ///
236    /// This enables navigation up the object hierarchy (e.g., Pane -> Tab -> Window).
237    ///
238    /// # Arguments
239    ///
240    /// * `handle` - The handle of the object whose parent to retrieve
241    ///
242    /// # Returns
243    ///
244    /// * `Ok(Some(handle))` - The parent object's handle
245    /// * `Ok(None)` - The object has no parent (e.g., Window is top-level)
246    /// * `Err(ObjectError)` - If the handle is invalid or not found
247    fn get_parent(&self, handle: &ObjectHandle) -> Result<Option<ObjectHandle>, ObjectError>;
248
249    /// Get the child handles of an object
250    ///
251    /// This enables navigation down the object hierarchy (e.g., Window -> Tabs -> Panes).
252    ///
253    /// # Arguments
254    ///
255    /// * `handle` - The handle of the object whose children to retrieve
256    ///
257    /// # Returns
258    ///
259    /// * `Ok(Vec<ObjectHandle>)` - A vector of child object handles (may be empty)
260    /// * `Err(ObjectError)` - If the handle is invalid or not found
261    fn get_children(&self, handle: &ObjectHandle) -> Result<Vec<ObjectHandle>, ObjectError>;
262}
263
264#[cfg(test)]
265mod tests {
266    use super::*;
267    use crate::object_model::handle::ObjectType;
268    use std::collections::HashMap;
269
270    // Simple test object
271    #[derive(Debug, Clone, PartialEq)]
272    struct TestObject {
273        name: String,
274        value: i32,
275    }
276
277    // Simple registry implementation for testing
278    struct TestRegistry {
279        objects: HashMap<u64, RegistryEntry<TestObject>>,
280        next_id_counter: u64,
281        generations: HashMap<u64, u32>,
282        parents: HashMap<u64, ObjectHandle>,
283        children: HashMap<u64, Vec<ObjectHandle>>,
284    }
285
286    impl TestRegistry {
287        fn new() -> Self {
288            Self {
289                objects: HashMap::new(),
290                next_id_counter: 1,
291                generations: HashMap::new(),
292                parents: HashMap::new(),
293                children: HashMap::new(),
294            }
295        }
296
297        fn set_parent(&mut self, child: ObjectHandle, parent: ObjectHandle) {
298            self.parents.insert(child.id(), parent);
299            self.children
300                .entry(parent.id())
301                .or_insert_with(Vec::new)
302                .push(child);
303        }
304    }
305
306    impl ObjectRegistry<TestObject> for TestRegistry {
307        fn register(&mut self, object: TestObject) -> ObjectHandle {
308            let id = self.next_id();
309            let generation = self.current_generation(id);
310            let handle = ObjectHandle::new(ObjectType::Window, id, generation);
311            self.objects.insert(id, RegistryEntry::new(handle, object));
312            handle
313        }
314
315        fn unregister(&mut self, handle: ObjectHandle) -> Result<TestObject, ObjectError> {
316            let current_gen = self.current_generation(handle.id());
317            if !handle.is_valid(current_gen) {
318                return Err(ObjectError::stale_handle(handle, current_gen));
319            }
320
321            self.increment_generation(handle.id());
322            self.objects
323                .remove(&handle.id())
324                .map(|entry| entry.into_object())
325                .ok_or_else(|| ObjectError::not_found(handle))
326        }
327
328        fn get(&self, handle: ObjectHandle) -> Result<&TestObject, ObjectError> {
329            let current_gen = self.current_generation(handle.id());
330            if !handle.is_valid(current_gen) {
331                return Err(ObjectError::stale_handle(handle, current_gen));
332            }
333
334            self.objects
335                .get(&handle.id())
336                .map(|entry| entry.object())
337                .ok_or_else(|| ObjectError::not_found(handle))
338        }
339
340        fn get_mut(&mut self, handle: ObjectHandle) -> Result<&mut TestObject, ObjectError> {
341            let current_gen = self.current_generation(handle.id());
342            if !handle.is_valid(current_gen) {
343                return Err(ObjectError::stale_handle(handle, current_gen));
344            }
345
346            self.objects
347                .get_mut(&handle.id())
348                .map(|entry| entry.object_mut())
349                .ok_or_else(|| ObjectError::not_found(handle))
350        }
351
352        fn get_by_id(&self, id: u64) -> Option<&TestObject> {
353            self.objects.get(&id).map(|entry| entry.object())
354        }
355
356        fn next_id(&mut self) -> u64 {
357            let id = self.next_id_counter;
358            self.next_id_counter += 1;
359            id
360        }
361
362        fn increment_generation(&mut self, id: u64) {
363            let gen = self.generations.entry(id).or_insert(0);
364            *gen = gen.wrapping_add(1);
365        }
366
367        fn current_generation(&self, id: u64) -> u32 {
368            self.generations.get(&id).copied().unwrap_or(0)
369        }
370
371        fn all_ids(&self) -> Vec<u64> {
372            self.objects.keys().copied().collect()
373        }
374
375        fn len(&self) -> usize {
376            self.objects.len()
377        }
378
379        fn get_parent(&self, handle: &ObjectHandle) -> Result<Option<ObjectHandle>, ObjectError> {
380            let current_gen = self.current_generation(handle.id());
381            if !handle.is_valid(current_gen) {
382                return Err(ObjectError::stale_handle(*handle, current_gen));
383            }
384
385            if !self.objects.contains_key(&handle.id()) {
386                return Err(ObjectError::not_found(*handle));
387            }
388
389            Ok(self.parents.get(&handle.id()).copied())
390        }
391
392        fn get_children(&self, handle: &ObjectHandle) -> Result<Vec<ObjectHandle>, ObjectError> {
393            let current_gen = self.current_generation(handle.id());
394            if !handle.is_valid(current_gen) {
395                return Err(ObjectError::stale_handle(*handle, current_gen));
396            }
397
398            if !self.objects.contains_key(&handle.id()) {
399                return Err(ObjectError::not_found(*handle));
400            }
401
402            Ok(self
403                .children
404                .get(&handle.id())
405                .cloned()
406                .unwrap_or_else(Vec::new))
407        }
408    }
409
410    #[test]
411    fn test_registry_entry_creation() {
412        let handle = ObjectHandle::new(ObjectType::Window, 1, 0);
413        let obj = TestObject {
414            name: "test".to_string(),
415            value: 42,
416        };
417        let entry = RegistryEntry::new(handle, obj.clone());
418
419        assert_eq!(entry.handle(), handle);
420        assert_eq!(entry.object(), &obj);
421    }
422
423    #[test]
424    fn test_registry_entry_mutability() {
425        let handle = ObjectHandle::new(ObjectType::Window, 1, 0);
426        let obj = TestObject {
427            name: "test".to_string(),
428            value: 42,
429        };
430        let mut entry = RegistryEntry::new(handle, obj);
431
432        entry.object_mut().value = 100;
433        assert_eq!(entry.object().value, 100);
434    }
435
436    #[test]
437    fn test_register_and_get() {
438        let mut registry = TestRegistry::new();
439        let obj = TestObject {
440            name: "test".to_string(),
441            value: 42,
442        };
443
444        let handle = registry.register(obj.clone());
445        let retrieved = registry.get(handle).unwrap();
446
447        assert_eq!(retrieved, &obj);
448    }
449
450    #[test]
451    fn test_unregister() {
452        let mut registry = TestRegistry::new();
453        let obj = TestObject {
454            name: "test".to_string(),
455            value: 42,
456        };
457
458        let handle = registry.register(obj.clone());
459        assert_eq!(registry.len(), 1);
460
461        let removed = registry.unregister(handle).unwrap();
462        assert_eq!(removed, obj);
463        assert_eq!(registry.len(), 0);
464    }
465
466    #[test]
467    fn test_stale_handle_after_unregister() {
468        let mut registry = TestRegistry::new();
469        let obj = TestObject {
470            name: "test".to_string(),
471            value: 42,
472        };
473
474        let handle = registry.register(obj.clone());
475        registry.unregister(handle).unwrap();
476
477        // Old handle should now be stale
478        let result = registry.get(handle);
479        assert!(matches!(result, Err(ObjectError::StaleHandle { .. })));
480    }
481
482    #[test]
483    fn test_generation_increment() {
484        let mut registry = TestRegistry::new();
485
486        let obj1 = TestObject {
487            name: "first".to_string(),
488            value: 1,
489        };
490        let obj2 = TestObject {
491            name: "second".to_string(),
492            value: 2,
493        };
494
495        let handle1 = registry.register(obj1);
496        let id = handle1.id();
497
498        registry.unregister(handle1).unwrap();
499
500        // The first ID's generation should have been incremented
501        assert_eq!(registry.current_generation(id), 1);
502
503        // Second object gets a new ID (simple registry doesn't reuse IDs)
504        let handle2 = registry.register(obj2);
505        assert_ne!(handle2.id(), id);
506        assert_eq!(handle2.generation(), 0); // New ID starts at generation 0
507    }
508
509    #[test]
510    fn test_get_by_id() {
511        let mut registry = TestRegistry::new();
512        let obj = TestObject {
513            name: "test".to_string(),
514            value: 42,
515        };
516
517        let handle = registry.register(obj.clone());
518        let retrieved = registry.get_by_id(handle.id()).unwrap();
519
520        assert_eq!(retrieved, &obj);
521    }
522
523    #[test]
524    fn test_is_valid() {
525        let mut registry = TestRegistry::new();
526        let obj = TestObject {
527            name: "test".to_string(),
528            value: 42,
529        };
530
531        let handle = registry.register(obj);
532        assert!(registry.is_valid(handle));
533
534        registry.unregister(handle).unwrap();
535        assert!(!registry.is_valid(handle));
536    }
537
538    #[test]
539    fn test_all_ids() {
540        let mut registry = TestRegistry::new();
541
542        let h1 = registry.register(TestObject {
543            name: "one".to_string(),
544            value: 1,
545        });
546        let h2 = registry.register(TestObject {
547            name: "two".to_string(),
548            value: 2,
549        });
550
551        let mut ids = registry.all_ids();
552        ids.sort();
553
554        assert_eq!(ids.len(), 2);
555        assert!(ids.contains(&h1.id()));
556        assert!(ids.contains(&h2.id()));
557    }
558
559    #[test]
560    fn test_len_and_is_empty() {
561        let mut registry = TestRegistry::new();
562        assert!(registry.is_empty());
563        assert_eq!(registry.len(), 0);
564
565        let handle = registry.register(TestObject {
566            name: "test".to_string(),
567            value: 42,
568        });
569        assert!(!registry.is_empty());
570        assert_eq!(registry.len(), 1);
571
572        registry.unregister(handle).unwrap();
573        assert!(registry.is_empty());
574        assert_eq!(registry.len(), 0);
575    }
576
577    #[test]
578    fn test_get_mut() {
579        let mut registry = TestRegistry::new();
580        let obj = TestObject {
581            name: "test".to_string(),
582            value: 42,
583        };
584
585        let handle = registry.register(obj);
586
587        {
588            let obj_mut = registry.get_mut(handle).unwrap();
589            obj_mut.value = 100;
590        }
591
592        let obj = registry.get(handle).unwrap();
593        assert_eq!(obj.value, 100);
594    }
595
596    #[test]
597    fn test_get_parent_none() {
598        let mut registry = TestRegistry::new();
599        let obj = TestObject {
600            name: "root".to_string(),
601            value: 1,
602        };
603
604        let handle = registry.register(obj);
605        let parent = registry.get_parent(&handle).unwrap();
606        assert!(parent.is_none());
607    }
608
609    #[test]
610    fn test_get_parent_some() {
611        let mut registry = TestRegistry::new();
612
613        let parent_obj = TestObject {
614            name: "parent".to_string(),
615            value: 1,
616        };
617        let child_obj = TestObject {
618            name: "child".to_string(),
619            value: 2,
620        };
621
622        let parent_handle = registry.register(parent_obj);
623        let child_handle = registry.register(child_obj);
624
625        registry.set_parent(child_handle, parent_handle);
626
627        let retrieved_parent = registry.get_parent(&child_handle).unwrap();
628        assert_eq!(retrieved_parent, Some(parent_handle));
629    }
630
631    #[test]
632    fn test_get_parent_stale_handle() {
633        let mut registry = TestRegistry::new();
634        let obj = TestObject {
635            name: "test".to_string(),
636            value: 42,
637        };
638
639        let handle = registry.register(obj);
640        registry.unregister(handle).unwrap();
641
642        let result = registry.get_parent(&handle);
643        assert!(matches!(result, Err(ObjectError::StaleHandle { .. })));
644    }
645
646    #[test]
647    fn test_get_children_empty() {
648        let mut registry = TestRegistry::new();
649        let obj = TestObject {
650            name: "leaf".to_string(),
651            value: 1,
652        };
653
654        let handle = registry.register(obj);
655        let children = registry.get_children(&handle).unwrap();
656        assert!(children.is_empty());
657    }
658
659    #[test]
660    fn test_get_children_multiple() {
661        let mut registry = TestRegistry::new();
662
663        let parent_obj = TestObject {
664            name: "parent".to_string(),
665            value: 1,
666        };
667        let child1_obj = TestObject {
668            name: "child1".to_string(),
669            value: 2,
670        };
671        let child2_obj = TestObject {
672            name: "child2".to_string(),
673            value: 3,
674        };
675
676        let parent_handle = registry.register(parent_obj);
677        let child1_handle = registry.register(child1_obj);
678        let child2_handle = registry.register(child2_obj);
679
680        registry.set_parent(child1_handle, parent_handle);
681        registry.set_parent(child2_handle, parent_handle);
682
683        let children = registry.get_children(&parent_handle).unwrap();
684        assert_eq!(children.len(), 2);
685        assert!(children.contains(&child1_handle));
686        assert!(children.contains(&child2_handle));
687    }
688
689    #[test]
690    fn test_get_children_stale_handle() {
691        let mut registry = TestRegistry::new();
692        let obj = TestObject {
693            name: "test".to_string(),
694            value: 42,
695        };
696
697        let handle = registry.register(obj);
698        registry.unregister(handle).unwrap();
699
700        let result = registry.get_children(&handle);
701        assert!(matches!(result, Err(ObjectError::StaleHandle { .. })));
702    }
703
704    #[test]
705    fn test_navigation_hierarchy() {
706        let mut registry = TestRegistry::new();
707
708        // Create a hierarchy: window -> tab -> pane
709        let window_handle = registry.register(TestObject {
710            name: "window".to_string(),
711            value: 1,
712        });
713        let tab_handle = registry.register(TestObject {
714            name: "tab".to_string(),
715            value: 2,
716        });
717        let pane_handle = registry.register(TestObject {
718            name: "pane".to_string(),
719            value: 3,
720        });
721
722        registry.set_parent(tab_handle, window_handle);
723        registry.set_parent(pane_handle, tab_handle);
724
725        // Navigate up from pane to tab
726        let pane_parent = registry.get_parent(&pane_handle).unwrap().unwrap();
727        assert_eq!(pane_parent, tab_handle);
728
729        // Navigate up from tab to window
730        let tab_parent = registry.get_parent(&tab_handle).unwrap().unwrap();
731        assert_eq!(tab_parent, window_handle);
732
733        // Window has no parent
734        let window_parent = registry.get_parent(&window_handle).unwrap();
735        assert!(window_parent.is_none());
736
737        // Navigate down from window to tab
738        let window_children = registry.get_children(&window_handle).unwrap();
739        assert_eq!(window_children.len(), 1);
740        assert_eq!(window_children[0], tab_handle);
741
742        // Navigate down from tab to pane
743        let tab_children = registry.get_children(&tab_handle).unwrap();
744        assert_eq!(tab_children.len(), 1);
745        assert_eq!(tab_children[0], pane_handle);
746    }
747}