Skip to main content

dynamo_runtime/pipeline/
registry.rs

1// SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::any::Any;
5use std::collections::HashMap;
6use std::sync::Arc;
7
8/// Registry struct that manages both shared and unique objects.
9///
10/// # Examples
11///
12/// ```
13/// use dynamo_runtime::pipeline::registry::Registry;
14///
15/// let mut registry = Registry::new();
16///
17/// // Insert and retrieve shared objects
18/// registry.insert_shared("shared1", 42);
19/// assert_eq!(*registry.get_shared::<i32>("shared1").unwrap(), 42);
20///
21/// // Insert and take unique objects
22/// registry.insert_unique("unique1", "Hello".to_string());
23/// assert_eq!(registry.take_unique::<String>("unique1").unwrap(), "Hello");
24///
25/// // Taking the same unique again should fail since it's not cloneable
26/// assert!(registry.take_unique::<String>("unique1").is_err());
27///
28/// // Insert and clone unique objects
29/// registry.insert_unique("unique2", "World".to_string());
30/// assert_eq!(registry.clone_unique::<String>("unique2").unwrap(), "World");
31///
32/// // Taking the same cloned unique should is ok
33/// assert!(registry.take_unique::<String>("unique2").is_ok());
34///
35/// ```
36#[derive(Debug, Default)]
37pub struct Registry {
38    shared_storage: HashMap<String, Arc<dyn Any + Send + Sync>>, // Shared objects
39    unique_storage: HashMap<String, Box<dyn Any + Send + Sync>>, // Takable objects
40}
41
42impl Registry {
43    /// Create a new empty registry.
44    pub fn new() -> Self {
45        Registry {
46            shared_storage: HashMap::new(),
47            unique_storage: HashMap::new(),
48        }
49    }
50
51    /// Check if a shared object exists in the registry by key.
52    pub fn contains_shared(&self, key: &str) -> bool {
53        self.shared_storage.contains_key(key)
54    }
55
56    /// Insert a shared object into the registry with a specific key.
57    pub fn insert_shared<K: ToString, U: Send + Sync + 'static>(&mut self, key: K, value: U) {
58        self.shared_storage.insert(
59            key.to_string(),
60            Arc::new(value) as Arc<dyn Any + Send + Sync>,
61        );
62    }
63
64    /// Retrieve a shared object from the registry by key and type.
65    pub fn get_shared<V: Send + Sync + 'static>(&self, key: &str) -> Result<Arc<V>, String> {
66        self.get_shared_optional(key)?
67            .ok_or_else(|| format!("Shared key not found: {}", key))
68    }
69
70    /// Retrieve an optional shared object from the registry by key and type.
71    pub fn get_shared_optional<V: Send + Sync + 'static>(
72        &self,
73        key: &str,
74    ) -> Result<Option<Arc<V>>, String> {
75        let Some(boxed) = self.shared_storage.get(key) else {
76            return Ok(None);
77        };
78        boxed.clone().downcast::<V>().map(Some).map_err(|_| {
79            format!(
80                "Failed to downcast to the requested type for shared key: {}",
81                key
82            )
83        })
84    }
85
86    /// Check if a unique object exists in the registry by key.
87    pub fn contains_unique(&self, key: &str) -> bool {
88        self.unique_storage.contains_key(key)
89    }
90
91    /// Insert a unique object into the registry with a specific key.
92    pub fn insert_unique<K: ToString, U: Send + Sync + 'static>(&mut self, key: K, value: U) {
93        self.unique_storage.insert(
94            key.to_string(),
95            Box::new(value) as Box<dyn Any + Send + Sync>,
96        );
97    }
98
99    /// Take a unique object from the registry by key and type, removing it from the registry.
100    pub fn take_unique<V: Send + Sync + 'static>(&mut self, key: &str) -> Result<V, String> {
101        match self.unique_storage.remove(key) {
102            Some(boxed) => boxed.downcast::<V>().map(|b| *b).map_err(|_| {
103                format!(
104                    "Failed to downcast to the requested type for unique key: {}",
105                    key
106                )
107            }),
108            None => Err(format!("Takable key not found: {}", key)),
109        }
110    }
111
112    /// Clone a unique object from the registry if it implements `Clone`.
113    pub fn clone_unique<V: Clone + Send + Sync + 'static>(&self, key: &str) -> Result<V, String> {
114        match self.unique_storage.get(key) {
115            Some(boxed) => boxed.downcast_ref::<V>().cloned().ok_or_else(|| {
116                format!(
117                    "Failed to downcast to the requested type for unique key: {}",
118                    key
119                )
120            }),
121            None => Err(format!("Takable key not found: {}", key)),
122        }
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129
130    #[test]
131    fn test_insert_and_get_shared() {
132        let mut registry = Registry::new();
133        registry.insert_shared("shared1", 42);
134        assert_eq!(*registry.get_shared::<i32>("shared1").unwrap(), 42);
135        assert!(registry.get_shared::<f64>("shared1").is_err()); // Testing a downcast failure
136    }
137
138    #[test]
139    fn test_get_optional_shared() {
140        let mut registry = Registry::new();
141        assert!(
142            registry
143                .get_shared_optional::<i32>("missing")
144                .unwrap()
145                .is_none()
146        );
147
148        registry.insert_shared("shared1", 42);
149        assert_eq!(
150            *registry
151                .get_shared_optional::<i32>("shared1")
152                .unwrap()
153                .unwrap(),
154            42
155        );
156        assert!(registry.get_shared_optional::<f64>("shared1").is_err());
157    }
158
159    #[test]
160    fn test_insert_and_take_unique() {
161        let mut registry = Registry::new();
162        registry.insert_unique("unique1", "Hello".to_string());
163        assert_eq!(registry.take_unique::<String>("unique1").unwrap(), "Hello");
164        assert!(registry.take_unique::<String>("unique1").is_err()); // Key is now missing
165    }
166
167    #[test]
168    fn test_insert_and_clone_then_take_unique() {
169        let mut registry = Registry::new();
170
171        registry.insert_unique("unique2", "World".to_string());
172
173        assert_eq!(registry.clone_unique::<String>("unique2").unwrap(), "World");
174
175        // When cloned, the object should still be available for taking
176        assert!(registry.take_unique::<String>("unique2").is_ok());
177    }
178
179    #[test]
180    fn test_failed_take_after_cloning() {
181        let mut registry = Registry::new();
182
183        registry.insert_unique("unique3", "Another".to_string());
184        assert_eq!(
185            registry.clone_unique::<String>("unique3").unwrap(),
186            "Another"
187        );
188
189        // Cloned, then Take is OK
190        assert_eq!(
191            registry.take_unique::<String>("unique3").unwrap(),
192            "Another"
193        );
194
195        // Take, then Take again should fail
196        assert!(registry.take_unique::<String>("unique3").is_err());
197    }
198}