Skip to main content

camel_bean/
registry.rs

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}