pub struct TaskContext<'a> { /* private fields */ }Expand description
Per-call execution context handed to AsyncFunctionHandler::execute.
Borrows the message and datalogic engine for the duration of the handler
call; collects Change entries that the workflow executor folds into the
audit trail when the handler returns. Drop semantics are trivial — there
is nothing to flush; the executor extracts the buffered changes via
into_changes().
Implementations§
Source§impl<'a> TaskContext<'a>
impl<'a> TaskContext<'a>
Sourcepub fn new(
message: &'a mut Message,
datalogic: &'a Arc<DatalogicEngine>,
) -> Self
pub fn new( message: &'a mut Message, datalogic: &'a Arc<DatalogicEngine>, ) -> Self
Construct a new context. Mostly engine-internal — handlers receive a
pre-built &mut TaskContext from the executor — but exposed pub so
tests and benchmarks can drive AsyncFunctionHandler::execute
directly without going through Engine::process_message.
A context built this way reports None from Self::workflow_id,
Self::task_id and Self::loop_counter — there is no workflow run
to describe, and inventing ids would be worse than admitting their
absence.
Sourcepub fn workflow_id(&self) -> Option<&str>
pub fn workflow_id(&self) -> Option<&str>
Id of the workflow being executed, when the engine built this context.
None for a context built with Self::new — a test or benchmark
driving a handler directly is not inside a workflow run.
let ctx = TaskContext::new(&mut message, &datalogic);
assert_eq!(ctx.workflow_id(), None);Sourcepub fn task_id(&self) -> Option<&str>
pub fn task_id(&self) -> Option<&str>
Id of the task being executed, when the engine built this context.
Always a leaf task’s id. Handlers run only on leaf tasks — a task group is span bookkeeping recorded on the task that opens it, never a dispatch target — so a group id can never appear here.
None for a context built with Self::new.
Sourcepub fn loop_counter(&self) -> Option<i64>
pub fn loop_counter(&self) -> Option<i64>
Sweep index of the enclosing looping workflow, or None when the
workflow does not carry a loop.
This is a different fact from identity being unknown: a handler in a non-looping workflow has both ids and no counter.
Worth preferring over reading the counter out of temp_data, which
only works when the host gave LoopConfig a counter name and the
handler hardcodes that path. A loop with no named counter writes to no
path at all, and its sweep index is reachable no other way.
Sourcepub fn element_index(&self) -> Option<usize>
pub fn element_index(&self) -> Option<usize>
Index of the element this call runs for, when the task carries a
for_each; None otherwise.
The same value the engine binds at temp_data.<as>_index in this
call’s view of the message, without the handler having to know the
as name.
let ctx = TaskContext::new(&mut message, &datalogic);
assert_eq!(ctx.element_index(), None);Sourcepub fn secret(&self, name: &str) -> Option<&OwnedDataValue>
pub fn secret(&self, name: &str) -> Option<&OwnedDataValue>
A secret by dotted name, from the store the host configured through
crate::EngineBuilder::with_secrets.
For handlers whose config names a key ("key_name": "partner_hmac")
rather than embedding a crate::Template — a Template field can
simply read {"secret": "partner_hmac"} and needs nothing here.
None when the key is not declared, and always None for a context
built with Self::new. The contract for what you do with the value is
one line: a handler must not write a secret-derived value into the
message. Nothing in the engine records what this returns; whether it
stays unrecorded is the handler’s business from here on.
Sourcepub fn message(&self) -> &Message
pub fn message(&self) -> &Message
Borrow the message under processing. Use this when you need to inspect
the message id, payload, or audit trail; for reading and mutating the
data / metadata / temp_data context, prefer the typed helpers on
TaskContext itself.
Inside a for_each call this is the call’s own copy,
whose audit trail and errors start empty: it carries the context, id
and payload, not the message’s history.
Sourcepub fn message_mut(&mut self) -> &mut Message
pub fn message_mut(&mut self) -> &mut Message
Mutable access to the message. Prefer the typed helpers (set,
add_error) over poking at message.context directly — direct
mutations bypass the audit trail.
Sourcepub fn datalogic(&self) -> &Arc<DatalogicEngine> ⓘ
pub fn datalogic(&self) -> &Arc<DatalogicEngine> ⓘ
Shared datalogic engine, in case the handler needs to evaluate ad-hoc JSONLogic. Most handlers can ignore this argument.
Sourcepub fn data(&self) -> &OwnedDataValue
pub fn data(&self) -> &OwnedDataValue
Read-only view of data. Returns &OwnedDataValue::Null if missing
(mirrors the Index fallback semantics of serde_json::Value).
Sourcepub fn metadata(&self) -> &OwnedDataValue
pub fn metadata(&self) -> &OwnedDataValue
Read-only view of metadata.
Sourcepub fn temp_data(&self) -> &OwnedDataValue
pub fn temp_data(&self) -> &OwnedDataValue
Read-only view of temp_data.
Sourcepub fn context(&self) -> &OwnedDataValue
pub fn context(&self) -> &OwnedDataValue
The full {data, metadata, temp_data} tree — the root every workflow
JSONLogic expression is written against.
Self::data / Self::metadata / Self::temp_data expose the three
slots individually; this is the whole-context accessor, so handlers do not
have to reach through ctx.message().context.
Note payload is not part of this tree, and therefore not part of the
JSONLogic evaluation context — {"var": "payload.foo"} resolves to
nothing. Parse the payload into data first.
Sourcepub fn eval(&self, logic: &Logic) -> Result<OwnedDataValue>
pub fn eval(&self, logic: &Logic) -> Result<OwnedDataValue>
Evaluate a pre-compiled expression against the message context, on the worker thread’s pooled arena.
The same path crate::engine::executor::evaluate_condition takes, but
returning the value instead of collapsing it to a bool, and surfacing
evaluation failures as Err instead of false. That difference is
deliberate: a condition that fails to evaluate should not run its task,
whereas a handler reading a config value needs to know the read failed.
§Errors
crate::DataflowError::LogicEvaluation if the expression fails to
evaluate, or crate::DataflowError::BudgetExceeded if it was aborted
for crossing the ceiling set by
crate::EngineBuilder::with_ops_budget.
Sourcepub fn eval_json(&self, logic: &Logic) -> Result<JsonValue>
pub fn eval_json(&self, logic: &Logic) -> Result<JsonValue>
As Self::eval, projected straight from the arena to
serde_json::Value in one walk — no OwnedDataValue intermediate and no
serde_json::from_value rebuild.
Sourcepub fn eval_to_plain_string(&self, logic: &Logic) -> Result<String>
pub fn eval_to_plain_string(&self, logic: &Logic) -> Result<String>
As Self::eval, coerced to a plain string: a JSON string result
yields its contents, anything else its compact JSON form.
§This disagrees with datalogic-rs on purpose
datalogic-rs’s String: FromDataValue — and therefore
Session::eval_str — keeps the JSON quoting, so a string result comes
back from it as "\"abc\"". This method returns abc.
The name says plain_string rather than to_string precisely so the
difference is visible at the call site: two string semantics in one
ecosystem is a footgun, and these values end up in URL paths and message
keys. A test pins both sides, so it fails if either changes.
Sourcepub fn with_view<R>(&self, f: impl FnOnce(&ContextView<'_>) -> R) -> R
pub fn with_view<R>(&self, f: impl FnOnce(&ContextView<'_>) -> R) -> R
Run f against one arena view of the message context, so several
expressions cost one view of the context instead of one each.
Every Self::eval (and every Template::resolve on a field that
reads the message) builds an arena view of the whole context before it
evaluates, so a step costs time in proportion to the number of arrays
and objects in the message, not to what it reads. A handler resolving k
templated fields pays for k views. Inside with_view it pays for one:
resolve the fields with Template::resolve_in and friends, or
evaluate directly with ContextView::eval.
Strings, keys and tensor buffers are borrowed rather than copied
(datavalue::OwnedDataValue::view_in), on this path and on the
per-call one, so what a view saves is the spine walk, not the bytes.
The view borrows self, so nothing can write to the message while it
is alive, and the closure is synchronous, so the view cannot be held
across an .await. Resolve what the call needs up front, then do the
I/O:
let (url, timeout_ms) = ctx.with_view(|view| {
Ok::<_, dataflow_rs::DataflowError>((
url.resolve_string_in(view)?,
timeout.resolve_u64_in(view, "timeout_ms")?,
))
})?;
// ... await the call with `url` and `timeout_ms` ...Sourcepub fn get(&self, path: &str) -> Option<&OwnedDataValue>
pub fn get(&self, path: &str) -> Option<&OwnedDataValue>
Look up a value by dot-path against the full context tree (rooted at
the unified {data, metadata, temp_data} object). Returns None if
the path doesn’t resolve.
Use the same path syntax as JSONLogic: "data.user.name",
"temp_data.items.0", "metadata.progress.status_code".
Sourcepub fn set(&mut self, path: &str, value: OwnedDataValue)
pub fn set(&mut self, path: &str, value: OwnedDataValue)
Set a value at a dot-path on the context. Records a Change on the
audit trail when message.capture_changes is true; otherwise the
write happens but no audit entry is buffered.
Intermediate objects/arrays are created on demand; see
crate::engine::utils::set_nested_value for the exact semantics
(numeric segments → arrays, # prefix → escaped object key, etc.).
Sourcepub fn set_json(&mut self, path: &str, value: &JsonValue)
pub fn set_json(&mut self, path: &str, value: &JsonValue)
Same as Self::set but accepts a serde_json::Value (bridges
through OwnedDataValue::from). Convenience for handlers that
already speak serde_json::Value.
Sourcepub fn add_error(&mut self, error: ErrorInfo)
pub fn add_error(&mut self, error: ErrorInfo)
Append an error to message.errors. Convenience for
ctx.message_mut().add_error(...).
Sourcepub fn into_changes(self) -> Vec<Change>
pub fn into_changes(self) -> Vec<Change>
Drain the accumulated changes. The workflow executor calls this after the handler returns to fold them into the audit trail; tests and benchmarks driving the trait directly can use it to inspect what the handler buffered.