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