camel_core/shared/components/domain/
registry.rs1use std::collections::HashMap;
2use std::sync::Arc;
3
4use camel_api::CamelError;
5use camel_api::component_metadata::ComponentMetadata;
6use camel_component_api::Component;
7
8pub struct Registry {
15 components: HashMap<String, Arc<dyn Component>>,
16 metadata: HashMap<String, ComponentMetadata>,
17}
18
19impl Registry {
20 pub fn new() -> Self {
22 Self {
23 components: HashMap::new(),
24 metadata: HashMap::new(),
25 }
26 }
27
28 pub fn register(&mut self, component: Arc<dyn Component>) {
35 let scheme = component.scheme().to_string();
36 let mut metadata = component.metadata();
37 if let Err(e) = metadata.validate_scheme(&scheme) {
38 tracing::warn!(scheme = %scheme, error = %e, "metadata scheme mismatch, normalizing");
39 metadata.scheme = scheme.clone();
40 }
41 self.metadata.insert(scheme.clone(), metadata);
42 self.components.insert(scheme, component);
43 }
44
45 pub fn get(&self, scheme: &str) -> Option<Arc<dyn Component>> {
47 self.components.get(scheme).cloned()
48 }
49
50 pub fn get_or_err(&self, scheme: &str) -> Result<Arc<dyn Component>, CamelError> {
52 self.get(scheme)
53 .ok_or_else(|| CamelError::ComponentNotFound(scheme.to_string()))
54 }
55
56 pub fn get_metadata(&self, scheme: &str) -> Option<ComponentMetadata> {
58 self.metadata.get(scheme).cloned()
59 }
60
61 pub fn all_metadata(&self) -> Vec<ComponentMetadata> {
63 self.metadata.values().cloned().collect()
64 }
65
66 pub fn metadata_schemes(&self) -> Vec<String> {
68 self.metadata.keys().cloned().collect()
69 }
70
71 pub fn len(&self) -> usize {
73 self.components.len()
74 }
75
76 pub fn is_empty(&self) -> bool {
78 self.components.is_empty()
79 }
80}
81
82impl Default for Registry {
83 fn default() -> Self {
84 Self::new()
85 }
86}
87
88pub struct RegistryComponentContext {
94 registry: Arc<std::sync::Mutex<Registry>>,
95}
96
97impl RegistryComponentContext {
98 pub fn new(registry: Arc<std::sync::Mutex<Registry>>) -> Self {
99 Self { registry }
100 }
101}
102
103impl camel_component_api::ComponentContext for RegistryComponentContext {
104 fn resolve_component(&self, scheme: &str) -> Option<Arc<dyn camel_component_api::Component>> {
105 self.registry.lock().ok()?.get(scheme)
106 }
107
108 fn resolve_language(&self, _name: &str) -> Option<Arc<dyn camel_language_api::Language>> {
109 None
110 }
111
112 fn metrics(&self) -> Arc<dyn camel_api::MetricsCollector> {
113 Arc::new(camel_api::NoOpMetrics)
114 }
115
116 fn platform_service(&self) -> Arc<dyn camel_api::PlatformService> {
117 Arc::new(camel_api::NoopPlatformService::default())
118 }
119
120 fn register_route_health_check(
121 &self,
122 _route_id: &str,
123 _check: Arc<dyn camel_api::AsyncHealthCheck>,
124 ) {
125 }
126
127 fn unregister_route_health_check(&self, _route_id: &str) {}
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133 use camel_api::component_metadata::ComponentMetadata;
134 use camel_component_log::LogComponent;
135 use camel_component_timer::TimerComponent;
136
137 #[test]
138 fn registry_starts_empty() {
139 let registry = Registry::new();
140 assert!(registry.is_empty());
141 assert_eq!(registry.len(), 0);
142 assert!(registry.get("timer").is_none());
143 }
144
145 #[test]
146 fn registry_registers_and_gets_components() {
147 let mut registry = Registry::new();
148 registry.register(Arc::new(TimerComponent::new()));
149 registry.register(Arc::new(LogComponent::new()));
150
151 assert_eq!(registry.len(), 2);
152 assert!(registry.get("timer").is_some());
153 assert!(registry.get("log").is_some());
154 assert!(!registry.is_empty());
155 }
156
157 #[test]
158 fn registry_get_or_err_reports_missing_component() {
159 let mut registry = Registry::new();
160 registry.register(Arc::new(TimerComponent::new()));
161
162 let err = match registry.get_or_err("missing") {
163 Ok(_) => panic!("must fail"),
164 Err(err) => err,
165 };
166 assert!(matches!(err, CamelError::ComponentNotFound(_)));
167 }
168
169 #[test]
170 fn registry_replaces_component_with_same_scheme() {
171 let mut registry = Registry::new();
172 registry.register(Arc::new(TimerComponent::new()));
173 registry.register(Arc::new(TimerComponent::new()));
174
175 assert_eq!(registry.len(), 1);
176 assert!(registry.get("timer").is_some());
177 assert_eq!(registry.all_metadata().len(), 1);
178 }
179
180 #[test]
181 fn registry_harvests_metadata_on_register() {
182 let mut registry = Registry::new();
183 registry.register(Arc::new(TimerComponent::new()));
184
185 let meta = registry.get_metadata("timer");
186 assert!(meta.is_some());
187 let meta = meta.unwrap(); assert_eq!(meta.scheme, "timer");
189 assert_eq!(meta.schema_version, ComponentMetadata::SCHEMA_VERSION);
190 }
191
192 #[test]
193 fn registry_all_metadata_returns_all_schemes() {
194 let mut registry = Registry::new();
195 registry.register(Arc::new(TimerComponent::new()));
196 registry.register(Arc::new(LogComponent::new()));
197
198 let all = registry.all_metadata();
199 assert_eq!(all.len(), 2);
200 }
201
202 #[test]
203 fn registry_metadata_schemes_lists_all_keys() {
204 let mut registry = Registry::new();
205 registry.register(Arc::new(TimerComponent::new()));
206 registry.register(Arc::new(LogComponent::new()));
207
208 let mut schemes = registry.metadata_schemes();
209 schemes.sort();
210 assert_eq!(schemes, vec!["log".to_string(), "timer".to_string()]);
211 }
212}