camel_processor/do_try.rs
1//! Shared helpers for the doTry segment (`do_try_segment.rs`).
2//!
3//! [`CatchMatcher`] matches a `doCatch` clause against a `CamelError`.
4//! `chain_predicate_error` attaches the original try error as the `cause`
5//! of a failed catch `when` / `on_when` predicate (language-value-boundary
6//! envelope). The runtime itself lives in `DoTrySegment`.
7
8use camel_api::{CamelError, Exchange, PredicateSource};
9
10/// Matcher for a `doCatch` clause.
11#[derive(Clone)]
12pub enum CatchMatcher {
13 /// Match by CamelError variant name (e.g. ["ProcessorError", "Io"]).
14 /// `"*"` matches any variant — equivalent to Camel's `doCatch(Throwable.class)`.
15 ByVariant(Vec<String>),
16 /// Match by a fallible predicate over the Exchange.
17 Predicate(PredicateSource),
18}
19
20impl CatchMatcher {
21 /// Returns whether this matcher matches the given error and exchange.
22 ///
23 /// A failed predicate evaluation yields `Err` (language-value-boundary:
24 /// the caller chains the original error and fails the scope).
25 pub async fn matches(&self, err: &CamelError, ex: &Exchange) -> Result<bool, CamelError> {
26 match self {
27 CatchMatcher::ByVariant(names) => {
28 if names.iter().any(|n| n == "*") {
29 return Ok(true);
30 }
31 Ok(names.iter().any(|n| n == err.variant_name()))
32 }
33 CatchMatcher::Predicate(source) => source.matches(ex).await,
34 }
35 }
36}
37
38/// Chain the original try error into a failed catch-predicate error
39/// (language-value-boundary: catch `when` / `on_when` failure envelope).
40///
41/// The copy keeps every field unchanged except `cause`, which holds the
42/// original error so the chain stays retrievable. Two kinds of cause are
43/// replaced: an empty slot (`cause: None`) and a mapper-generated diagnostic
44/// cause identified by the camel-language-api provenance marker (never by
45/// variant or string shape). Genuine caller-supplied chains — including
46/// hand-built `ProcessorError`/`ProcessorErrorWithSource` causes — pass
47/// through untouched. Errors that carry no `cause` slot pass through
48/// untouched as well.
49pub(crate) fn chain_predicate_error(
50 mut predicate_err: CamelError,
51 original: CamelError,
52) -> CamelError {
53 let should_chain = match &predicate_err {
54 CamelError::ExpressionFailed { cause, .. } => {
55 cause.is_none() || camel_language_api::is_expression_detail_cause(&predicate_err)
56 }
57 _ => false,
58 };
59 if should_chain && let CamelError::ExpressionFailed { cause, .. } = &mut predicate_err {
60 *cause = Some(Box::new(original));
61 }
62 predicate_err
63}