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