camel_api/
recipient_list.rs1use std::sync::Arc;
2
3use crate::Exchange;
4use crate::error::CamelError;
5
6pub type RecipientListExpression = Arc<dyn Fn(&Exchange) -> String + Send + Sync>;
7
8pub const DEFAULT_MAX_RECIPIENTS: usize = 1_000;
15
16#[derive(Clone)]
17pub struct RecipientListConfig {
18 pub expression: RecipientListExpression,
19 pub delimiter: String,
20 pub parallel: bool,
21 pub parallel_limit: Option<usize>,
22 pub stop_on_exception: bool,
23 pub strategy: crate::MulticastStrategy,
24 pub max_recipients: usize,
29}
30
31impl RecipientListConfig {
32 pub fn new(expression: RecipientListExpression) -> Self {
33 Self {
34 expression,
35 delimiter: ",".to_string(),
36 parallel: false,
37 parallel_limit: None,
38 stop_on_exception: false,
39 strategy: crate::MulticastStrategy::default(),
40 max_recipients: DEFAULT_MAX_RECIPIENTS,
41 }
42 }
43
44 pub fn delimiter(mut self, d: impl Into<String>) -> Self {
45 self.delimiter = d.into();
46 self
47 }
48
49 pub fn parallel(mut self, parallel: bool) -> Self {
50 self.parallel = parallel;
51 self
52 }
53
54 pub fn parallel_limit(mut self, limit: usize) -> Self {
55 self.parallel_limit = Some(limit);
56 self
57 }
58
59 pub fn stop_on_exception(mut self, stop: bool) -> Self {
60 self.stop_on_exception = stop;
61 self
62 }
63
64 pub fn strategy(mut self, strategy: crate::MulticastStrategy) -> Self {
65 self.strategy = strategy;
66 self
67 }
68
69 pub fn max_recipients(mut self, cap: usize) -> Self {
73 self.max_recipients = cap;
74 self
75 }
76
77 pub fn validate(&self) -> Result<(), CamelError> {
83 if self.parallel && self.parallel_limit == Some(0) {
84 return Err(CamelError::Config(
85 "recipient_list parallel_limit must be > 0".to_string(),
86 ));
87 }
88 if self.max_recipients == 0 {
89 return Err(CamelError::Config(
90 "recipient_list max_recipients must be > 0".to_string(),
91 ));
92 }
93 Ok(())
94 }
95}
96
97impl std::fmt::Debug for RecipientListConfig {
98 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99 f.debug_struct("RecipientListConfig")
100 .field("delimiter", &self.delimiter)
101 .field("parallel", &self.parallel)
102 .field("parallel_limit", &self.parallel_limit)
103 .field("stop_on_exception", &self.stop_on_exception)
104 .field("max_recipients", &self.max_recipients)
105 .finish()
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use std::sync::Arc;
112
113 use super::*;
114
115 fn noop_expr() -> RecipientListExpression {
116 Arc::new(|_| String::new())
117 }
118
119 #[test]
120 fn new_has_defaults() {
121 let cfg = RecipientListConfig::new(noop_expr());
122 assert_eq!(cfg.delimiter, ",");
123 assert!(!cfg.parallel);
124 assert!(cfg.parallel_limit.is_none());
125 assert!(!cfg.stop_on_exception);
126 }
127
128 #[test]
129 fn builder_chaining() {
130 let cfg = RecipientListConfig::new(noop_expr())
131 .delimiter(";")
132 .parallel(true)
133 .parallel_limit(4)
134 .stop_on_exception(true)
135 .strategy(crate::MulticastStrategy::CollectAll);
136 assert_eq!(cfg.delimiter, ";");
137 assert!(cfg.parallel);
138 assert_eq!(cfg.parallel_limit, Some(4));
139 assert!(cfg.stop_on_exception);
140 }
141
142 #[test]
143 fn clone_preserves_values() {
144 let cfg = RecipientListConfig::new(noop_expr())
145 .delimiter("|")
146 .parallel(true);
147 let cloned = cfg.clone();
148 assert_eq!(cloned.delimiter, "|");
149 assert!(cloned.parallel);
150 }
151
152 #[test]
153 fn debug_format() {
154 let cfg = RecipientListConfig::new(noop_expr());
155 let debug = format!("{cfg:?}");
156 assert!(debug.contains("RecipientListConfig"));
157 assert!(debug.contains("delimiter"));
158 }
159
160 #[test]
165 fn test_recipient_list_max_recipients_default() {
166 let cfg = RecipientListConfig::new(noop_expr());
167 assert_eq!(cfg.max_recipients, 1_000);
168 }
169
170 #[test]
173 fn test_recipient_list_validate_rejects_zero_cap() {
174 let cfg = RecipientListConfig::new(noop_expr()).max_recipients(0);
175 assert!(cfg.validate().is_err());
176 }
177}