1use std::any::Any;
2use std::collections::HashMap;
3use std::sync::Arc;
4
5use opentelemetry::Context;
6use uuid::Uuid;
7
8use crate::error::CamelError;
9use crate::from_body::FromBody;
10use crate::message::Message;
11use crate::value::Value;
12
13pub const ORIGINAL_MESSAGE_EXTENSION: &str = "CamelOriginalMessage";
16
17pub const CAMEL_STOP: &str = "CamelStop";
22
23pub fn is_camel_stop(exchange: &Exchange) -> bool {
29 exchange
30 .property(CAMEL_STOP)
31 .and_then(|v| v.as_bool())
32 .unwrap_or(false)
33}
34
35pub const PROPERTY_EXCEPTION_MESSAGE: &str = "CamelExceptionMessage";
37pub const PROPERTY_EXCEPTION_KIND: &str = "CamelExceptionKind";
39pub const PROPERTY_EXCEPTION_CAUGHT: &str = "CamelExceptionCaught";
41pub const PROPERTY_EXCEPTION_HANDLED: &str = "CamelExceptionHandled";
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
47pub enum ExchangePattern {
48 #[default]
50 InOnly,
51 InOut,
53}
54
55#[derive(Debug)]
60pub struct Exchange {
61 pub input: Message,
63 pub output: Option<Message>,
65 pub properties: HashMap<String, Value>,
67 pub extensions: HashMap<String, Arc<dyn Any + Send + Sync>>,
70 pub error: Option<CamelError>,
72 pub pattern: ExchangePattern,
74 pub correlation_id: String,
76 pub otel_context: Context,
80}
81
82impl Exchange {
83 pub fn new(input: Message) -> Self {
85 Self {
86 input,
87 output: None,
88 properties: HashMap::new(),
89 extensions: HashMap::new(),
90 error: None,
91 pattern: ExchangePattern::default(),
92 correlation_id: Uuid::new_v4().to_string(),
93 otel_context: Context::new(),
94 }
95 }
96
97 pub fn new_in_out(input: Message) -> Self {
99 Self {
100 input,
101 output: None,
102 properties: HashMap::new(),
103 extensions: HashMap::new(),
104 error: None,
105 pattern: ExchangePattern::InOut,
106 correlation_id: Uuid::new_v4().to_string(),
107 otel_context: Context::new(),
108 }
109 }
110
111 pub fn correlation_id(&self) -> &str {
113 &self.correlation_id
114 }
115
116 pub fn property(&self, key: &str) -> Option<&Value> {
118 self.properties.get(key)
119 }
120
121 pub fn set_property(&mut self, key: impl Into<String>, value: impl Into<Value>) {
123 self.properties.insert(key.into(), value.into());
124 }
125
126 pub fn has_error(&self) -> bool {
128 self.error.is_some()
129 }
130
131 pub fn set_error(&mut self, error: CamelError) {
139 let msg = error.to_string();
140 let kind = error.classify().to_string();
141 self.properties.insert(
142 PROPERTY_EXCEPTION_MESSAGE.to_string(),
143 Value::String(msg.clone()),
144 );
145 self.properties
146 .insert(PROPERTY_EXCEPTION_KIND.to_string(), Value::String(kind));
147 self.properties
148 .insert(PROPERTY_EXCEPTION_CAUGHT.to_string(), Value::String(msg));
149 self.error = Some(error);
150 }
151
152 pub fn clear_error(&mut self) {
157 self.error = None;
158 self.properties.remove(PROPERTY_EXCEPTION_MESSAGE);
159 self.properties.remove(PROPERTY_EXCEPTION_KIND);
160 self.properties.remove(PROPERTY_EXCEPTION_CAUGHT);
161 }
162
163 pub fn handle_error(&mut self) {
168 self.properties
169 .insert(PROPERTY_EXCEPTION_HANDLED.to_string(), Value::Bool(true));
170 self.clear_error();
171 }
172
173 pub fn set_extension(&mut self, key: impl Into<String>, value: Arc<dyn Any + Send + Sync>) {
175 self.extensions.insert(key.into(), value);
176 }
177
178 pub fn get_extension<T: Any>(&self, key: &str) -> Option<&T> {
181 self.extensions.get(key)?.downcast_ref::<T>()
182 }
183
184 pub fn body_as<T: FromBody>(&self) -> Result<T, CamelError> {
196 T::from_body(&self.input.body)
197 }
198}
199
200impl Clone for Exchange {
201 fn clone(&self) -> Self {
202 Self {
203 input: self.input.clone(),
204 output: self.output.clone(),
205 properties: self.properties.clone(),
206 extensions: self.extensions.clone(), error: self.error.clone(),
208 pattern: self.pattern,
209 correlation_id: self.correlation_id.clone(),
210 otel_context: self.otel_context.clone(),
211 }
212 }
213}
214
215impl Default for Exchange {
216 fn default() -> Self {
217 Self::new(Message::default())
218 }
219}
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224 use crate::Body;
225 use serde_json::json;
226
227 #[test]
228 fn test_exchange_new() {
229 let msg = Message::new("test");
230 let ex = Exchange::new(msg);
231 assert_eq!(ex.input.body.as_text(), Some("test"));
232 assert!(ex.output.is_none());
233 assert!(!ex.has_error());
234 assert_eq!(ex.pattern, ExchangePattern::InOnly);
235 }
236
237 #[test]
238 fn test_exchange_in_out() {
239 let ex = Exchange::new_in_out(Message::default());
240 assert_eq!(ex.pattern, ExchangePattern::InOut);
241 }
242
243 #[test]
244 fn test_exchange_properties() {
245 let mut ex = Exchange::default();
246 ex.set_property("key", Value::Bool(true));
247 assert_eq!(ex.property("key"), Some(&Value::Bool(true)));
248 assert_eq!(ex.property("missing"), None);
249 }
250
251 #[test]
252 fn test_exchange_error() {
253 let mut ex = Exchange::default();
254 assert!(!ex.has_error());
255 ex.set_error(CamelError::ProcessorError("test".into()));
256 assert!(ex.has_error());
257 }
258
259 #[test]
260 fn test_set_error_populates_properties() {
261 let mut ex = Exchange::default();
262 ex.set_error(CamelError::ProcessorError("boom".into()));
263
264 assert!(ex.has_error());
265 assert_eq!(
266 ex.properties.get(PROPERTY_EXCEPTION_MESSAGE),
267 Some(&Value::String("Processor error: boom".to_string()))
268 );
269 assert_eq!(
270 ex.properties.get(PROPERTY_EXCEPTION_KIND),
271 Some(&Value::String("processor".to_string()))
272 );
273 assert_eq!(
274 ex.properties.get(PROPERTY_EXCEPTION_CAUGHT),
275 Some(&Value::String("Processor error: boom".to_string()))
276 );
277 }
278
279 #[test]
280 fn test_clear_error_removes_properties() {
281 let mut ex = Exchange::default();
282 ex.set_error(CamelError::RouteError("fail".into()));
283 assert!(ex.has_error());
284 assert!(ex.properties.contains_key(PROPERTY_EXCEPTION_MESSAGE));
285
286 ex.clear_error();
287
288 assert!(!ex.has_error());
289 assert!(!ex.properties.contains_key(PROPERTY_EXCEPTION_MESSAGE));
290 assert!(!ex.properties.contains_key(PROPERTY_EXCEPTION_KIND));
291 assert!(!ex.properties.contains_key(PROPERTY_EXCEPTION_CAUGHT));
292 }
293
294 #[test]
295 fn test_exchange_lifecycle() {
296 let mut ex = Exchange::new(Message::new("input data"));
297 assert_eq!(ex.input.body.as_text(), Some("input data"));
298
299 ex.set_property("processed", Value::Bool(true));
301
302 ex.output = Some(Message::new("output data"));
304 assert!(ex.output.is_some());
305
306 assert!(!ex.has_error());
308 }
309
310 #[test]
311 fn test_exchange_otel_context_default() {
312 let ex = Exchange::default();
313 use opentelemetry::trace::TraceContextExt;
316 assert!(!ex.otel_context.span().span_context().is_valid());
317 }
318
319 #[test]
320 fn test_exchange_otel_context_propagates_in_clone() {
321 let ex = Exchange::default();
322 let cloned = ex.clone();
323 use opentelemetry::trace::TraceContextExt;
325 assert!(!cloned.otel_context.span().span_context().is_valid());
326 }
327
328 #[test]
329 fn test_set_and_get_extension() {
330 use std::sync::Arc;
331 let mut ex = Exchange::default();
332 ex.set_extension("my.key", Arc::new(42u32));
333 let val: Option<&u32> = ex.get_extension("my.key");
334 assert_eq!(val, Some(&42u32));
335 }
336
337 #[test]
338 fn test_get_extension_wrong_type_returns_none() {
339 use std::sync::Arc;
340 let mut ex = Exchange::default();
341 ex.set_extension("my.key", Arc::new(42u32));
342 let val: Option<&String> = ex.get_extension("my.key");
343 assert!(val.is_none());
344 }
345
346 #[test]
347 fn test_get_extension_missing_key_returns_none() {
348 let ex = Exchange::default();
349 let val: Option<&u32> = ex.get_extension("nope");
350 assert!(val.is_none());
351 }
352
353 #[test]
354 fn test_clone_shares_extension_arc() {
355 use std::sync::Arc;
356 let mut ex = Exchange::default();
357 ex.set_extension("shared", Arc::new(99u64));
358 let cloned = ex.clone();
359 assert_eq!(ex.get_extension::<u64>("shared"), Some(&99u64));
361 assert_eq!(cloned.get_extension::<u64>("shared"), Some(&99u64));
362 }
363
364 #[test]
365 fn test_body_as_string_from_text() {
366 let ex = Exchange::new(Message::new(Body::Text("hello".to_string())));
367
368 let result = ex.body_as::<String>();
369
370 assert_eq!(result.unwrap(), "hello");
371 }
372
373 #[test]
374 fn test_body_as_string_from_json_string() {
375 let ex = Exchange::new(Message::new(Body::Json(json!("hello"))));
376
377 let result = ex.body_as::<String>();
378
379 assert_eq!(result.unwrap(), "hello");
380 }
381
382 #[test]
383 fn test_body_as_json_value_from_json_number() {
384 let ex = Exchange::new(Message::new(Body::Json(json!(42))));
385
386 let result = ex.body_as::<serde_json::Value>();
387
388 assert_eq!(result.unwrap(), json!(42));
389 }
390
391 #[test]
392 fn test_body_as_vec_u8_from_bytes() {
393 let ex = Exchange::new(Message::new(Body::from(vec![1u8, 2, 3, 4])));
394
395 let result = ex.body_as::<Vec<u8>>();
396
397 assert_eq!(result.unwrap(), vec![1u8, 2, 3, 4]);
398 }
399
400 #[test]
401 fn test_body_as_string_from_empty_returns_err() {
402 let ex = Exchange::new(Message::new(Body::Empty));
403
404 let result = ex.body_as::<String>();
405
406 assert!(result.is_err());
407 }
408}