1use crate::{BeanError, BeanProcessor};
2use camel_api::{CamelError, Exchange};
3use std::collections::HashMap;
4use std::sync::Arc;
5use tracing::{debug, warn};
6
7pub struct BeanRegistry {
8 beans: HashMap<String, Arc<dyn BeanProcessor>>,
9}
10
11impl BeanRegistry {
12 pub fn new() -> Self {
13 Self {
14 beans: HashMap::new(),
15 }
16 }
17
18 pub fn register<B>(&mut self, name: impl Into<String>, bean: B) -> Result<(), BeanError>
19 where
20 B: BeanProcessor + 'static,
21 {
22 let name = name.into();
23 if name.trim().is_empty() {
24 return Err(BeanError::InvalidName(name));
25 }
26 if self.beans.contains_key(&name) {
27 warn!(bean_name = %name, "bean already registered");
28 return Err(BeanError::DuplicateName(name));
29 }
30 self.beans.insert(name.clone(), Arc::new(bean));
31 debug!(bean_name = %name, "bean registered");
32 Ok(())
33 }
34
35 pub fn get(&self, name: &str) -> Option<Arc<dyn BeanProcessor>> {
36 self.beans.get(name).cloned()
37 }
38
39 pub async fn invoke(
40 &self,
41 bean_name: &str,
42 method: &str,
43 exchange: &mut Exchange,
44 ) -> Result<(), CamelError> {
45 debug!(bean_name = %bean_name, method = %method, "invoking bean");
46
47 let bean = self.get(bean_name).ok_or_else(|| {
48 warn!(bean_name = %bean_name, "bean not found");
49 BeanError::NotFound(bean_name.to_string())
50 })?;
51
52 if !bean.methods().iter().any(|m| m == method) {
53 warn!(bean_name = %bean_name, method = %method, "bean method not found");
54 return Err(BeanError::MethodNotFound(method.to_string()).into());
55 }
56
57 bean.call(method, exchange).await.map_err(|err| {
58 warn!(bean_name = %bean_name, method = %method, error = %err, "bean invocation failed");
59 err
60 })
61 }
62
63 pub fn len(&self) -> usize {
64 self.beans.len()
65 }
66
67 pub fn is_empty(&self) -> bool {
68 self.beans.is_empty()
69 }
70}
71
72impl Default for BeanRegistry {
73 fn default() -> Self {
74 Self::new()
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81 use async_trait::async_trait;
82 use camel_api::{Exchange, Message};
83
84 struct MockBean;
85
86 struct ErrorBean;
87
88 #[async_trait]
89 impl BeanProcessor for MockBean {
90 async fn call(&self, method: &str, _exchange: &mut Exchange) -> Result<(), CamelError> {
91 match method {
92 "process" => Ok(()),
93 _ => Err(BeanError::MethodNotFound(method.to_string()).into()),
94 }
95 }
96
97 fn methods(&self) -> Vec<String> {
98 vec!["process".to_string()]
99 }
100 }
101
102 #[async_trait]
103 impl BeanProcessor for ErrorBean {
104 async fn call(&self, _method: &str, _exchange: &mut Exchange) -> Result<(), CamelError> {
105 Err(CamelError::ProcessorError("boom".to_string()))
106 }
107
108 fn methods(&self) -> Vec<String> {
109 vec!["process".to_string()]
110 }
111 }
112
113 #[test]
114 fn test_default_is_empty() {
115 let registry = BeanRegistry::default();
116 assert!(registry.is_empty());
117 assert_eq!(registry.len(), 0);
118 }
119
120 #[test]
121 fn test_register_and_get() {
122 let mut registry = BeanRegistry::new();
123 registry.register("mock", MockBean).unwrap();
124
125 assert!(registry.get("mock").is_some());
126 assert!(registry.get("nonexistent").is_none());
127 }
128
129 #[test]
130 fn test_len_and_is_empty() {
131 let mut registry = BeanRegistry::new();
132 assert!(registry.is_empty());
133
134 registry.register("mock", MockBean).unwrap();
135 assert_eq!(registry.len(), 1);
136 assert!(!registry.is_empty());
137 }
138
139 #[test]
140 fn test_register_same_name_rejects() {
141 let mut registry = BeanRegistry::new();
142 registry.register("dup", MockBean).unwrap();
143 let result = registry.register("dup", ErrorBean);
144 assert!(result.is_err());
145 assert_eq!(registry.len(), 1);
146 }
147
148 #[test]
149 fn test_register_duplicate_returns_error() {
150 let mut registry = BeanRegistry::new();
151 registry.register("dup", MockBean).unwrap();
152 let err = registry.register("dup", ErrorBean).unwrap_err();
153 assert!(matches!(err, BeanError::DuplicateName(_)));
154 }
155
156 #[tokio::test]
157 async fn test_invoke_success() {
158 let mut registry = BeanRegistry::new();
159 registry.register("mock", MockBean).unwrap();
160
161 let mut exchange = Exchange::new(Message::default());
162 let result = registry.invoke("mock", "process", &mut exchange).await;
163
164 assert!(result.is_ok());
165 }
166
167 #[tokio::test]
168 async fn test_invoke_bean_not_found() {
169 let registry = BeanRegistry::new();
170 let mut exchange = Exchange::new(Message::default());
171
172 let result = registry
173 .invoke("nonexistent", "process", &mut exchange)
174 .await;
175 assert!(result.is_err());
176 }
177
178 #[tokio::test]
179 async fn test_invoke_method_not_found() {
180 let mut registry = BeanRegistry::new();
181 registry.register("mock", MockBean).unwrap();
182
183 let mut exchange = Exchange::new(Message::default());
184 let result = registry.invoke("mock", "unknown", &mut exchange).await;
185
186 assert!(result.is_err());
187 }
188
189 #[test]
190 fn test_register_empty_name_rejected() {
191 let mut registry = BeanRegistry::new();
192 let result = registry.register("", MockBean);
193 assert!(result.is_err());
194 assert!(matches!(result.unwrap_err(), BeanError::InvalidName(_)));
195 }
196
197 #[test]
198 fn test_register_whitespace_name_rejected() {
199 let mut registry = BeanRegistry::new();
200 let result = registry.register(" ", MockBean);
201 assert!(result.is_err());
202 assert!(matches!(result.unwrap_err(), BeanError::InvalidName(_)));
203 }
204
205 #[tokio::test]
206 async fn test_invoke_propagates_bean_error() {
207 let mut registry = BeanRegistry::new();
208 registry.register("err", ErrorBean).unwrap();
209
210 let mut exchange = Exchange::new(Message::default());
211 let result = registry.invoke("err", "process", &mut exchange).await;
212
213 assert!(matches!(result, Err(CamelError::ProcessorError(_))));
214 }
215}