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