Skip to main content

camel_api/
recipient_list.rs

1use std::sync::Arc;
2
3use crate::Exchange;
4use crate::error::CamelError;
5
6pub type RecipientListExpression = Arc<dyn Fn(&Exchange) -> String + Send + Sync>;
7
8#[derive(Clone)]
9pub struct RecipientListConfig {
10    pub expression: RecipientListExpression,
11    pub delimiter: String,
12    pub parallel: bool,
13    pub parallel_limit: Option<usize>,
14    pub stop_on_exception: bool,
15    pub strategy: crate::MulticastStrategy,
16}
17
18impl RecipientListConfig {
19    pub fn new(expression: RecipientListExpression) -> Self {
20        Self {
21            expression,
22            delimiter: ",".to_string(),
23            parallel: false,
24            parallel_limit: None,
25            stop_on_exception: false,
26            strategy: crate::MulticastStrategy::default(),
27        }
28    }
29
30    pub fn delimiter(mut self, d: impl Into<String>) -> Self {
31        self.delimiter = d.into();
32        self
33    }
34
35    pub fn parallel(mut self, parallel: bool) -> Self {
36        self.parallel = parallel;
37        self
38    }
39
40    pub fn parallel_limit(mut self, limit: usize) -> Self {
41        self.parallel_limit = Some(limit);
42        self
43    }
44
45    pub fn stop_on_exception(mut self, stop: bool) -> Self {
46        self.stop_on_exception = stop;
47        self
48    }
49
50    pub fn strategy(mut self, strategy: crate::MulticastStrategy) -> Self {
51        self.strategy = strategy;
52        self
53    }
54
55    /// Validates the configuration.
56    ///
57    /// Returns `Err(CamelError::Config)` if `parallel_limit` is set to 0,
58    /// which would cause incorrect concurrency behavior at runtime.
59    pub fn validate(&self) -> Result<(), CamelError> {
60        if self.parallel && self.parallel_limit == Some(0) {
61            return Err(CamelError::Config(
62                "recipient_list parallel_limit must be > 0".to_string(),
63            ));
64        }
65        Ok(())
66    }
67}
68
69impl std::fmt::Debug for RecipientListConfig {
70    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71        f.debug_struct("RecipientListConfig")
72            .field("delimiter", &self.delimiter)
73            .field("parallel", &self.parallel)
74            .field("parallel_limit", &self.parallel_limit)
75            .field("stop_on_exception", &self.stop_on_exception)
76            .finish()
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use std::sync::Arc;
83
84    use super::*;
85
86    fn noop_expr() -> RecipientListExpression {
87        Arc::new(|_| String::new())
88    }
89
90    #[test]
91    fn new_has_defaults() {
92        let cfg = RecipientListConfig::new(noop_expr());
93        assert_eq!(cfg.delimiter, ",");
94        assert!(!cfg.parallel);
95        assert!(cfg.parallel_limit.is_none());
96        assert!(!cfg.stop_on_exception);
97    }
98
99    #[test]
100    fn builder_chaining() {
101        let cfg = RecipientListConfig::new(noop_expr())
102            .delimiter(";")
103            .parallel(true)
104            .parallel_limit(4)
105            .stop_on_exception(true)
106            .strategy(crate::MulticastStrategy::CollectAll);
107        assert_eq!(cfg.delimiter, ";");
108        assert!(cfg.parallel);
109        assert_eq!(cfg.parallel_limit, Some(4));
110        assert!(cfg.stop_on_exception);
111    }
112
113    #[test]
114    fn clone_preserves_values() {
115        let cfg = RecipientListConfig::new(noop_expr())
116            .delimiter("|")
117            .parallel(true);
118        let cloned = cfg.clone();
119        assert_eq!(cloned.delimiter, "|");
120        assert!(cloned.parallel);
121    }
122
123    #[test]
124    fn debug_format() {
125        let cfg = RecipientListConfig::new(noop_expr());
126        let debug = format!("{cfg:?}");
127        assert!(debug.contains("RecipientListConfig"));
128        assert!(debug.contains("delimiter"));
129    }
130}