Skip to main content

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}