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 pub in_flight_claim: Option<crate::in_flight::InFlightClaim>,
89}
90
91impl Exchange {
92 pub fn new(input: Message) -> Self {
94 Self {
95 input,
96 output: None,
97 properties: HashMap::new(),
98 extensions: HashMap::new(),
99 error: None,
100 pattern: ExchangePattern::default(),
101 correlation_id: Uuid::new_v4().to_string(),
102 otel_context: Context::new(),
103 in_flight_claim: None,
104 }
105 }
106
107 pub fn new_in_out(input: Message) -> Self {
109 Self {
110 input,
111 output: None,
112 properties: HashMap::new(),
113 extensions: HashMap::new(),
114 error: None,
115 pattern: ExchangePattern::InOut,
116 correlation_id: Uuid::new_v4().to_string(),
117 otel_context: Context::new(),
118 in_flight_claim: None,
119 }
120 }
121
122 pub fn correlation_id(&self) -> &str {
124 &self.correlation_id
125 }
126
127 pub fn property(&self, key: &str) -> Option<&Value> {
129 self.properties.get(key)
130 }
131
132 pub fn set_property(&mut self, key: impl Into<String>, value: impl Into<Value>) {
134 self.properties.insert(key.into(), value.into());
135 }
136
137 pub fn has_error(&self) -> bool {
139 self.error.is_some()
140 }
141
142 pub fn set_error(&mut self, error: CamelError) {
150 let msg = error.to_string();
151 let kind = error.classify().to_string();
152 self.properties.insert(
153 PROPERTY_EXCEPTION_MESSAGE.to_string(),
154 Value::String(msg.clone()),
155 );
156 self.properties
157 .insert(PROPERTY_EXCEPTION_KIND.to_string(), Value::String(kind));
158 self.properties
159 .insert(PROPERTY_EXCEPTION_CAUGHT.to_string(), Value::String(msg));
160 self.error = Some(error);
161 }
162
163 pub fn clear_error(&mut self) {
168 self.error = None;
169 self.properties.remove(PROPERTY_EXCEPTION_MESSAGE);
170 self.properties.remove(PROPERTY_EXCEPTION_KIND);
171 self.properties.remove(PROPERTY_EXCEPTION_CAUGHT);
172 }
173
174 pub fn handle_error(&mut self) {
179 self.properties
180 .insert(PROPERTY_EXCEPTION_HANDLED.to_string(), Value::Bool(true));
181 self.clear_error();
182 }
183
184 pub fn set_extension(&mut self, key: impl Into<String>, value: Arc<dyn Any + Send + Sync>) {
186 self.extensions.insert(key.into(), value);
187 }
188
189 pub fn get_extension<T: Any>(&self, key: &str) -> Option<&T> {
192 self.extensions.get(key)?.downcast_ref::<T>()
193 }
194
195 pub fn body_as<T: FromBody>(&self) -> Result<T, CamelError> {
207 T::from_body(&self.input.body)
208 }
209}
210
211impl Clone for Exchange {
212 fn clone(&self) -> Self {
213 Self {
214 input: self.input.clone(),
215 output: self.output.clone(),
216 properties: self.properties.clone(),
217 extensions: self.extensions.clone(), error: self.error.clone(),
219 pattern: self.pattern,
220 correlation_id: self.correlation_id.clone(),
221 otel_context: self.otel_context.clone(),
222 in_flight_claim: None,
226 }
227 }
228}
229
230impl Default for Exchange {
231 fn default() -> Self {
232 Self::new(Message::default())
233 }
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239 use crate::Body;
240 use serde_json::json;
241
242 #[test]
243 fn test_exchange_new() {
244 let msg = Message::new("test");
245 let ex = Exchange::new(msg);
246 assert_eq!(ex.input.body.as_text(), Some("test"));
247 assert!(ex.output.is_none());
248 assert!(!ex.has_error());
249 assert_eq!(ex.pattern, ExchangePattern::InOnly);
250 }
251
252 #[test]
253 fn test_exchange_in_out() {
254 let ex = Exchange::new_in_out(Message::default());
255 assert_eq!(ex.pattern, ExchangePattern::InOut);
256 }
257
258 #[test]
259 fn test_exchange_properties() {
260 let mut ex = Exchange::default();
261 ex.set_property("key", Value::Bool(true));
262 assert_eq!(ex.property("key"), Some(&Value::Bool(true)));
263 assert_eq!(ex.property("missing"), None);
264 }
265
266 #[test]
267 fn test_exchange_error() {
268 let mut ex = Exchange::default();
269 assert!(!ex.has_error());
270 ex.set_error(CamelError::ProcessorError("test".into()));
271 assert!(ex.has_error());
272 }
273
274 #[test]
275 fn test_set_error_populates_properties() {
276 let mut ex = Exchange::default();
277 ex.set_error(CamelError::ProcessorError("boom".into()));
278
279 assert!(ex.has_error());
280 assert_eq!(
281 ex.properties.get(PROPERTY_EXCEPTION_MESSAGE),
282 Some(&Value::String("Processor error: boom".to_string()))
283 );
284 assert_eq!(
285 ex.properties.get(PROPERTY_EXCEPTION_KIND),
286 Some(&Value::String("processor".to_string()))
287 );
288 assert_eq!(
289 ex.properties.get(PROPERTY_EXCEPTION_CAUGHT),
290 Some(&Value::String("Processor error: boom".to_string()))
291 );
292 }
293
294 #[test]
295 fn test_clear_error_removes_properties() {
296 let mut ex = Exchange::default();
297 ex.set_error(CamelError::RouteError("fail".into()));
298 assert!(ex.has_error());
299 assert!(ex.properties.contains_key(PROPERTY_EXCEPTION_MESSAGE));
300
301 ex.clear_error();
302
303 assert!(!ex.has_error());
304 assert!(!ex.properties.contains_key(PROPERTY_EXCEPTION_MESSAGE));
305 assert!(!ex.properties.contains_key(PROPERTY_EXCEPTION_KIND));
306 assert!(!ex.properties.contains_key(PROPERTY_EXCEPTION_CAUGHT));
307 }
308
309 #[test]
310 fn test_exchange_lifecycle() {
311 let mut ex = Exchange::new(Message::new("input data"));
312 assert_eq!(ex.input.body.as_text(), Some("input data"));
313
314 ex.set_property("processed", Value::Bool(true));
316
317 ex.output = Some(Message::new("output data"));
319 assert!(ex.output.is_some());
320
321 assert!(!ex.has_error());
323 }
324
325 #[test]
326 fn test_exchange_otel_context_default() {
327 let ex = Exchange::default();
328 use opentelemetry::trace::TraceContextExt;
331 assert!(!ex.otel_context.span().span_context().is_valid());
332 }
333
334 #[test]
335 fn test_exchange_otel_context_propagates_in_clone() {
336 let ex = Exchange::default();
337 let cloned = ex.clone();
338 use opentelemetry::trace::TraceContextExt;
340 assert!(!cloned.otel_context.span().span_context().is_valid());
341 }
342
343 #[test]
344 fn test_set_and_get_extension() {
345 use std::sync::Arc;
346 let mut ex = Exchange::default();
347 ex.set_extension("my.key", Arc::new(42u32));
348 let val: Option<&u32> = ex.get_extension("my.key");
349 assert_eq!(val, Some(&42u32));
350 }
351
352 #[test]
353 fn test_get_extension_wrong_type_returns_none() {
354 use std::sync::Arc;
355 let mut ex = Exchange::default();
356 ex.set_extension("my.key", Arc::new(42u32));
357 let val: Option<&String> = ex.get_extension("my.key");
358 assert!(val.is_none());
359 }
360
361 #[test]
362 fn test_get_extension_missing_key_returns_none() {
363 let ex = Exchange::default();
364 let val: Option<&u32> = ex.get_extension("nope");
365 assert!(val.is_none());
366 }
367
368 #[test]
369 fn test_clone_shares_extension_arc() {
370 use std::sync::Arc;
371 let mut ex = Exchange::default();
372 ex.set_extension("shared", Arc::new(99u64));
373 let cloned = ex.clone();
374 assert_eq!(ex.get_extension::<u64>("shared"), Some(&99u64));
376 assert_eq!(cloned.get_extension::<u64>("shared"), Some(&99u64));
377 }
378
379 #[test]
380 fn test_body_as_string_from_text() {
381 let ex = Exchange::new(Message::new(Body::Text("hello".to_string())));
382
383 let result = ex.body_as::<String>();
384
385 assert_eq!(result.unwrap(), "hello");
386 }
387
388 #[test]
389 fn test_body_as_string_from_json_string() {
390 let ex = Exchange::new(Message::new(Body::Json(json!("hello"))));
391
392 let result = ex.body_as::<String>();
393
394 assert_eq!(result.unwrap(), "hello");
395 }
396
397 #[test]
398 fn test_body_as_json_value_from_json_number() {
399 let ex = Exchange::new(Message::new(Body::Json(json!(42))));
400
401 let result = ex.body_as::<serde_json::Value>();
402
403 assert_eq!(result.unwrap(), json!(42));
404 }
405
406 #[test]
407 fn test_body_as_vec_u8_from_bytes() {
408 let ex = Exchange::new(Message::new(Body::from(vec![1u8, 2, 3, 4])));
409
410 let result = ex.body_as::<Vec<u8>>();
411
412 assert_eq!(result.unwrap(), vec![1u8, 2, 3, 4]);
413 }
414
415 #[test]
416 fn test_body_as_string_from_empty_returns_err() {
417 let ex = Exchange::new(Message::new(Body::Empty));
418
419 let result = ex.body_as::<String>();
420
421 assert!(result.is_err());
422 }
423}