Skip to main content

TaskContext

Struct TaskContext 

Source
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>

Source

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.

Source

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);
Source

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.

Source

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.

Source

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);
Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn data(&self) -> &OwnedDataValue

Read-only view of data. Returns &OwnedDataValue::Null if missing (mirrors the Index fallback semantics of serde_json::Value).

Source

pub fn metadata(&self) -> &OwnedDataValue

Read-only view of metadata.

Source

pub fn temp_data(&self) -> &OwnedDataValue

Read-only view of temp_data.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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` ...
Source

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".

Source

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.).

Source

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.

Source

pub fn add_error(&mut self, error: ErrorInfo)

Append an error to message.errors. Convenience for ctx.message_mut().add_error(...).

Source

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.

Auto Trait Implementations§

§

impl<'a> !RefUnwindSafe for TaskContext<'a>

§

impl<'a> !UnwindSafe for TaskContext<'a>

§

impl<'a> Freeze for TaskContext<'a>

§

impl<'a> Send for TaskContext<'a>

§

impl<'a> Sync for TaskContext<'a>

§

impl<'a> Unpin for TaskContext<'a>

§

impl<'a> UnsafeUnpin for TaskContext<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.