Skip to main content

clawft_types/
registry.rs

1//! Unified registry trait for key-value registries across WeftOS.
2//!
3//! Many subsystems maintain registries that map a string key to some
4//! value (services, tools, pipes, workspaces, etc.).  This module
5//! provides a common read-side trait so GUI introspection, health
6//! checks, and cross-crate tooling can enumerate registry contents
7//! without depending on each concrete type.
8//!
9//! # Design notes
10//!
11//! The write side (`register`, `unregister`) is intentionally excluded
12//! from the trait because the concrete registries diverge significantly:
13//!
14//! - Some derive the key from the value (`service.name()`).
15//! - Some auto-generate the key (`ProcessTable::allocate_pid`).
16//! - Some require `&mut self`, others use interior mutability (`DashMap`).
17//!
18//! The read side, however, is consistent: look up by key, list keys,
19//! check membership, count entries.
20
21/// Read-side interface shared by all key-value registries.
22///
23/// Implementors expose a uniform way to inspect registry contents
24/// without coupling callers to concrete storage (DashMap, HashMap,
25/// Vec, etc.).
26///
27/// The `Value` associated type is the *returned* value, which may
28/// differ from the stored value (e.g., `Arc<dyn Trait>` vs `&T`).
29///
30/// # Examples
31///
32/// ```ignore
33/// fn dump_registry(reg: &dyn Registry<Value = String>) {
34///     for key in reg.list_keys() {
35///         if let Some(val) = reg.get(&key) {
36///             println!("{key}: {val}");
37///         }
38///     }
39/// }
40/// ```
41pub trait Registry {
42    /// The value returned by [`get`](Registry::get).
43    ///
44    /// Use an owned type (`Arc<T>`, `T: Clone`) when the registry
45    /// uses interior mutability and cannot hand out references.
46    type Value;
47
48    /// Look up a value by its string key.
49    fn get(&self, key: &str) -> Option<Self::Value>;
50
51    /// Return all keys currently in the registry.
52    ///
53    /// The order is unspecified unless a concrete implementation
54    /// documents otherwise.
55    fn list_keys(&self) -> Vec<String>;
56
57    /// Check whether `key` is present.
58    ///
59    /// Default implementation delegates to [`get`](Registry::get),
60    /// but concrete types should override when a cheaper check exists.
61    fn contains(&self, key: &str) -> bool {
62        self.get(key).is_some()
63    }
64
65    /// Number of entries in the registry.
66    fn count(&self) -> usize {
67        self.list_keys().len()
68    }
69
70    /// Whether the registry is empty.
71    fn is_empty(&self) -> bool {
72        self.count() == 0
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79    use std::collections::BTreeMap;
80
81    /// Minimal concrete registry for testing the trait.
82    struct SimpleRegistry {
83        map: BTreeMap<String, String>,
84    }
85
86    impl Registry for SimpleRegistry {
87        type Value = String;
88
89        fn get(&self, key: &str) -> Option<String> {
90            self.map.get(key).cloned()
91        }
92
93        fn list_keys(&self) -> Vec<String> {
94            self.map.keys().cloned().collect()
95        }
96
97        fn count(&self) -> usize {
98            self.map.len()
99        }
100    }
101
102    #[test]
103    fn empty_registry() {
104        let reg = SimpleRegistry {
105            map: BTreeMap::new(),
106        };
107        assert!(reg.is_empty());
108        assert_eq!(reg.count(), 0);
109        assert!(reg.list_keys().is_empty());
110        assert!(!reg.contains("anything"));
111        assert!(reg.get("anything").is_none());
112    }
113
114    #[test]
115    fn basic_operations() {
116        let mut map = BTreeMap::new();
117        map.insert("alpha".into(), "one".into());
118        map.insert("beta".into(), "two".into());
119
120        let reg = SimpleRegistry { map };
121
122        assert!(!reg.is_empty());
123        assert_eq!(reg.count(), 2);
124        assert!(reg.contains("alpha"));
125        assert!(!reg.contains("gamma"));
126        assert_eq!(reg.get("beta"), Some("two".into()));
127        assert_eq!(reg.list_keys(), vec!["alpha", "beta"]);
128    }
129
130    #[test]
131    fn trait_object_works() {
132        let mut map = BTreeMap::new();
133        map.insert("key".into(), "val".into());
134        let reg = SimpleRegistry { map };
135
136        // Ensure it can be used as a trait object.
137        let dyn_reg: &dyn Registry<Value = String> = &reg;
138        assert_eq!(dyn_reg.count(), 1);
139        assert_eq!(dyn_reg.get("key"), Some("val".into()));
140    }
141}