Skip to main content

dex_sdk/
context.rs

1// Copyright (c) 2026 Super Durable, Inc.
2//
3// Licensed under the Super Durable Source License 1.0.
4// You may not use this file except in compliance with the License.
5// See the LICENSE file in the repository root.
6//
7// SPDX-License-Identifier: LicenseRef-Super-Durable-1.0
8
9use std::collections::{HashMap, HashSet};
10use std::sync::atomic::{AtomicBool, Ordering};
11use std::sync::{Arc, Condvar, Mutex};
12use std::time::{Duration, SystemTime};
13
14use dex_protocol::dex::{
15    AttributeWrite, ChannelInfo, ChannelMessage, ConditionResults, ConditionStatus,
16    Context as ProtoContext, Kv, StepStreamWrite, Value as ProtoValue, value,
17};
18
19use crate::persistence::PersistenceKind;
20use crate::registry::{RegisteredFlow, decode_instance, physical_name};
21use crate::value_mapper;
22use crate::worker_output::StepOutputEmitter;
23use crate::{
24    Attribute, AttributeMap, Channel, ChannelMap, FlowResult, HandlerError, HandlerResult, Stream,
25    Value,
26};
27
28#[derive(Clone, Copy, Eq, PartialEq)]
29pub(crate) enum InvocationMethod {
30    WaitFor,
31    Execute,
32    Rpc,
33}
34
35pub(crate) struct ContextInput {
36    pub(crate) method: InvocationMethod,
37    pub(crate) flow: RegisteredFlow,
38    pub(crate) metadata: ProtoContext,
39    pub(crate) attributes: Vec<Kv>,
40    pub(crate) locals: Vec<Kv>,
41    pub(crate) condition_results: Option<ConditionResults>,
42    pub(crate) channel_infos: HashMap<String, ChannelInfo>,
43}
44
45/// Provides invocation metadata and staged durable changes to Step and RPC handlers.
46///
47/// A Context belongs to one handler attempt and must not outlive the call. Attribute writes,
48/// Channel publications, locals, and events become visible atomically only when the handler returns
49/// successfully. Application values are freshly decoded and owned by the invocation.
50pub struct Context {
51    method: InvocationMethod,
52    flow: RegisteredFlow,
53    output_emitter: Option<StepOutputEmitter>,
54    metadata: ProtoContext,
55    attributes: HashMap<String, ProtoValue>,
56    locals: HashMap<String, ProtoValue>,
57    condition_results: Option<ConditionResults>,
58    channel_infos: HashMap<String, ChannelInfo>,
59    attribute_writes: HashMap<String, AttributeWrite>,
60    local_writes: HashMap<String, Kv>,
61    events: Vec<Kv>,
62    event_names: HashSet<String>,
63    publications: Vec<ChannelMessage>,
64    cancellation: InvocationCancellation,
65    runtime: Option<tokio::runtime::Handle>,
66    step_output_finalizers: Vec<Arc<dyn StepOutputFinalizer>>,
67    step_outputs_finalized: bool,
68}
69
70pub(crate) trait StepOutputFinalizer: Send + Sync {
71    fn finalize_step_output(&self) -> HandlerResult<()>;
72}
73
74impl Context {
75    pub(crate) fn new(
76        input: ContextInput,
77        output_emitter: Option<StepOutputEmitter>,
78        cancellation: InvocationCancellation,
79    ) -> HandlerResult<Self> {
80        Ok(Self {
81            method: input.method,
82            flow: input.flow,
83            output_emitter,
84            metadata: input.metadata,
85            attributes: map_values("Attribute", input.attributes)?,
86            locals: map_values("step-execution local", input.locals)?,
87            condition_results: input.condition_results,
88            channel_infos: input.channel_infos,
89            attribute_writes: HashMap::new(),
90            local_writes: HashMap::new(),
91            events: Vec::new(),
92            event_names: HashSet::new(),
93            publications: Vec::new(),
94            cancellation,
95            runtime: tokio::runtime::Handle::try_current().ok(),
96            step_output_finalizers: Vec::new(),
97            step_outputs_finalized: false,
98        })
99    }
100
101    /// Returns the application-assigned Flow ID.
102    pub fn flow_id(&self) -> &str {
103        &self.metadata.flow_id
104    }
105
106    /// Returns the server-assigned run ID for this execution.
107    pub fn run_id(&self) -> &str {
108        &self.metadata.run_id
109    }
110
111    /// Returns when the current Flow run started.
112    pub fn flow_started_at(&self) -> SystemTime {
113        system_time(self.metadata.flow_started_timestamp)
114    }
115
116    /// Returns the current Step execution ID, or an empty string for an RPC.
117    pub fn step_execution_id(&self) -> &str {
118        &self.metadata.step_execution_id
119    }
120
121    /// Returns the predecessor Step execution ID, or an empty string when absent.
122    pub fn from_step_execution_id(&self) -> &str {
123        &self.metadata.from_step_execution_id
124    }
125
126    /// Returns when the first attempt of this handler invocation started.
127    pub fn first_attempt_at(&self) -> SystemTime {
128        system_time(self.metadata.first_attempt_timestamp)
129    }
130
131    /// Returns the one-based handler attempt number.
132    pub fn attempt(&self) -> u32 {
133        u32::try_from(self.metadata.attempt).unwrap_or_default()
134    }
135
136    /// Records a heartbeat without a Value and clears persisted heartbeat details.
137    ///
138    /// The call blocks only while the Worker's one-frame output buffer is full. It does not wait
139    /// for Dex to process the heartbeat. Flow RPC Contexts reject this operation.
140    ///
141    /// # Errors
142    ///
143    /// Returns [`HandlerError`] for an RPC or Flow timeout Context, cancellation, or a closed
144    /// Worker response stream.
145    pub fn record_heartbeat(&mut self) -> HandlerResult<()> {
146        self.step_output_emitter()?.send_heartbeat(None)
147    }
148
149    /// Records a heartbeat carrying a typed checkpoint Value.
150    ///
151    /// A later regular attempt can decode the most recently accepted checkpoint with
152    /// [`Self::last_heartbeat_value`]. Local activities accept the frame but do not persist it.
153    /// The call uses the same bounded local buffering as [`Self::record_heartbeat`].
154    ///
155    /// # Examples
156    ///
157    /// ```
158    /// use dex_sdk::{Context, HandlerResult};
159    /// use serde::{Deserialize, Serialize};
160    ///
161    /// #[derive(Deserialize, Serialize)]
162    /// struct Checkpoint {
163    ///     offset: u64,
164    /// }
165    ///
166    /// fn import_batch(context: &mut Context, next_offset: u64) -> HandlerResult<()> {
167    ///     let previous = context.last_heartbeat_value::<Checkpoint>()?;
168    ///     let offset = previous.map_or(next_offset, |checkpoint| checkpoint.offset);
169    ///     context.record_heartbeat_value(Checkpoint { offset })
170    /// }
171    /// ```
172    ///
173    /// # Errors
174    ///
175    /// Returns [`HandlerError`] for an RPC or Flow timeout Context, encoding failure, cancellation,
176    /// or a closed Worker response stream.
177    pub fn record_heartbeat_value<T: Value>(&mut self, value: T) -> HandlerResult<()> {
178        let value = value_mapper::encode_handler(&value)?;
179        self.step_output_emitter()?.send_heartbeat(Some(value))
180    }
181
182    /// Decodes the checkpoint persisted by the previous regular attempt.
183    ///
184    /// `None` means no heartbeat Value is present. To distinguish an encoded JSON null, request an
185    /// optional application type: `last_heartbeat_value::<Option<T>>()` returns `Some(None)` for a
186    /// present null Value.
187    ///
188    /// # Errors
189    ///
190    /// Returns [`HandlerError`] for an RPC Context or when the persisted Value cannot be decoded
191    /// as `T`.
192    pub fn last_heartbeat_value<T: Value>(&self) -> HandlerResult<Option<T>> {
193        if self.method == InvocationMethod::Rpc {
194            return Err(HandlerError::new(
195                "dex_sdk::HandlerError",
196                "Heartbeat values require a Step Context",
197            ));
198        }
199        self.metadata
200            .last_heartbeat_value
201            .as_ref()
202            .map(value_mapper::decode_handler)
203            .transpose()
204    }
205
206    /// Returns whether any timer condition completed for this `execute` invocation.
207    pub fn has_any_timer_fired(&self) -> bool {
208        self.condition_results.as_ref().is_some_and(|results| {
209            results
210                .timer_results
211                .iter()
212                .any(|timer| timer.condition_status == ConditionStatus::Completed as i32)
213        })
214    }
215
216    /// Returns whether the zero-based timer at `index` completed.
217    ///
218    /// Missing indexes return `false`.
219    pub fn has_timer_fired(&self, index: usize) -> bool {
220        self.condition_results
221            .as_ref()
222            .and_then(|results| results.timer_results.get(index))
223            .is_some_and(|timer| timer.condition_status == ConditionStatus::Completed as i32)
224    }
225
226    /// Returns whether `execute` is running because `wait_for` exhausted retries with Proceed.
227    pub fn wait_for_method_failed(&self) -> bool {
228        self.condition_results
229            .as_ref()
230            .is_some_and(|results| results.wait_for_failed)
231    }
232
233    pub(crate) fn sub_flow_result(&self, index: usize) -> HandlerResult<FlowResult> {
234        if self.method != InvocationMethod::Execute {
235            return Err(HandlerError::new(
236                "dex_sdk::HandlerError",
237                "SubFlow results are available only during execute",
238            ));
239        }
240        let result = self
241            .condition_results
242            .as_ref()
243            .and_then(|results| results.sub_flow_results.get(index))
244            .ok_or_else(|| {
245                HandlerError::new(
246                    "dex_sdk::HandlerError",
247                    format!("SubFlow result index is unavailable: {index}"),
248                )
249            })?;
250        FlowResult::from_proto(result).map_err(HandlerError::from_error)
251    }
252
253    pub(crate) fn sub_flow_id(&self, index: usize) -> HandlerResult<String> {
254        self.sub_flow_result(index)?;
255        Ok(format!(
256            "SubFlow:{}-{}-{index}",
257            self.flow_id(),
258            self.step_execution_id()
259        ))
260    }
261
262    /// Blocks the current thread until Dex cancels this handler attempt.
263    ///
264    /// Use this only for interruptible application work; ordinary handlers should return normally.
265    pub fn wait_for_cancellation(&self) {
266        self.cancellation.wait();
267    }
268
269    /// Returns immediately with the current handler-cancellation state.
270    pub fn is_cancelled(&self) -> bool {
271        self.cancellation.is_cancelled()
272    }
273
274    /// Stages a Step-execution-local value visible to later attempts of the same execution.
275    ///
276    /// # Errors
277    ///
278    /// Returns [`HandlerError`] for an empty key or an encoding failure.
279    pub fn set_step_execution_local<T: Value>(&mut self, key: &str, value: T) -> HandlerResult<()> {
280        require_name(key, "step-execution local key")?;
281        let value = value_mapper::encode_handler(&value)?;
282        self.local_writes.insert(
283            key.to_string(),
284            Kv {
285                key: key.to_string(),
286                value: Some(value),
287            },
288        );
289        Ok(())
290    }
291
292    /// Reads a required Step-execution-local value, including writes staged by this attempt.
293    ///
294    /// # Errors
295    ///
296    /// Returns [`HandlerError`] for an empty key, a missing value, or a decoding failure.
297    pub fn step_execution_local<T: Value>(&self, key: &str) -> HandlerResult<T> {
298        require_name(key, "step-execution local key")?;
299        let value = self
300            .local_writes
301            .get(key)
302            .and_then(|entry| entry.value.as_ref())
303            .or_else(|| self.locals.get(key))
304            .ok_or_else(|| {
305                HandlerError::new(
306                    "dex_sdk::HandlerError",
307                    format!("step-execution local {key} is missing"),
308                )
309            })?;
310        value_mapper::decode_handler(value)
311    }
312
313    /// Records one named diagnostic event with a typed payload.
314    ///
315    /// Event names must be unique within the invocation.
316    ///
317    /// # Errors
318    ///
319    /// Returns [`HandlerError`] for an empty or duplicate name or an encoding failure.
320    pub fn record_event<T: Value>(&mut self, name: &str, value: T) -> HandlerResult<()> {
321        require_name(name, "event name")?;
322        if !self.event_names.insert(name.to_string()) {
323            return Err(HandlerError::new(
324                "dex_sdk::HandlerError",
325                format!("event was already recorded: {name}"),
326            ));
327        }
328        self.events.push(Kv {
329            key: name.to_string(),
330            value: Some(value_mapper::encode_handler(&value)?),
331        });
332        Ok(())
333    }
334
335    /// Reads an Attribute value, including writes staged by this invocation.
336    pub fn get_attribute<T: Value>(&self, attribute: &Attribute<T>) -> HandlerResult<Option<T>> {
337        self.get_attribute_value(attribute.name(), PersistenceKind::Attribute, None)
338    }
339
340    /// Reads one Attribute-map instance, including staged writes.
341    pub fn get_attribute_map<T: Value>(
342        &self,
343        attribute: &AttributeMap<T>,
344        instance: &str,
345    ) -> HandlerResult<Option<T>> {
346        self.get_attribute_value(
347            attribute.name(),
348            PersistenceKind::AttributeMap,
349            Some(instance),
350        )
351    }
352
353    /// Stages a typed Attribute write.
354    pub fn set_attribute<T: Value>(
355        &mut self,
356        attribute: &Attribute<T>,
357        value: T,
358    ) -> HandlerResult<()> {
359        self.set_attribute_value(
360            attribute.name(),
361            PersistenceKind::Attribute,
362            None,
363            value_mapper::encode_handler(&value)?,
364            attribute.index().map(|index| index.proto_config(false)),
365            attribute.sync_config(),
366        )
367    }
368
369    /// Stages a typed write for one Attribute-map instance.
370    pub fn set_attribute_map<T: Value>(
371        &mut self,
372        attribute: &AttributeMap<T>,
373        instance: &str,
374        value: T,
375    ) -> HandlerResult<()> {
376        self.set_attribute_value(
377            attribute.name(),
378            PersistenceKind::AttributeMap,
379            Some(instance),
380            value_mapper::encode_handler(&value)?,
381            attribute.index().map(|index| index.proto_config(true)),
382            attribute.sync_config(),
383        )
384    }
385
386    /// Stages deletion of an Attribute value.
387    pub fn delete_attribute<T>(&mut self, attribute: &Attribute<T>) -> HandlerResult<()> {
388        self.set_attribute_value(
389            attribute.name(),
390            PersistenceKind::Attribute,
391            None,
392            value_mapper::deletion(),
393            attribute.index().map(|index| index.proto_config(false)),
394            attribute.sync_config(),
395        )
396    }
397
398    /// Stages deletion of one Attribute-map instance.
399    pub fn delete_attribute_map<T>(
400        &mut self,
401        attribute: &AttributeMap<T>,
402        instance: &str,
403    ) -> HandlerResult<()> {
404        self.set_attribute_value(
405            attribute.name(),
406            PersistenceKind::AttributeMap,
407            Some(instance),
408            value_mapper::deletion(),
409            attribute.index().map(|index| index.proto_config(true)),
410            attribute.sync_config(),
411        )
412    }
413
414    pub(crate) fn attribute_map_keys<T>(
415        &self,
416        attribute: &AttributeMap<T>,
417    ) -> HandlerResult<Vec<String>> {
418        self.registered_name(
419            attribute.name(),
420            PersistenceKind::AttributeMap,
421            Some("key-check"),
422        )?;
423        let prefix = format!("{}/", attribute.name());
424        let mut physical_keys = self
425            .attributes
426            .keys()
427            .filter(|key| key.starts_with(&prefix))
428            .cloned()
429            .collect::<HashSet<_>>();
430        for (key, write) in &self.attribute_writes {
431            if !key.starts_with(&prefix) {
432                continue;
433            }
434            if matches!(
435                write.value.as_ref().and_then(|value| value.kind.as_ref()),
436                Some(value::Kind::NullValue(_))
437            ) {
438                physical_keys.remove(key);
439            } else {
440                physical_keys.insert(key.clone());
441            }
442        }
443        sorted_instance_keys(&prefix, physical_keys)
444    }
445
446    /// Stages one typed Channel publication.
447    pub fn publish<T: Value>(&mut self, channel: &Channel<T>, value: T) -> HandlerResult<()> {
448        self.publish_value(
449            channel.name(),
450            PersistenceKind::Channel,
451            None,
452            value_mapper::encode_handler(&value)?,
453        )
454    }
455
456    /// Stages one typed publication to a Channel-map instance.
457    pub fn publish_map<T: Value>(
458        &mut self,
459        channel: &ChannelMap<T>,
460        instance: &str,
461        value: T,
462    ) -> HandlerResult<()> {
463        self.publish_value(
464            channel.name(),
465            PersistenceKind::ChannelMap,
466            Some(instance),
467            value_mapper::encode_handler(&value)?,
468        )
469    }
470
471    /// Returns the current invocation snapshot's Channel queue size.
472    pub fn channel_size<T>(&self, channel: &Channel<T>) -> HandlerResult<usize> {
473        self.channel_size_value(channel.name(), PersistenceKind::Channel, None)
474    }
475
476    /// Returns one Channel-map instance's queue size.
477    pub fn channel_map_size<T>(
478        &self,
479        channel: &ChannelMap<T>,
480        instance: &str,
481    ) -> HandlerResult<usize> {
482        self.channel_size_value(channel.name(), PersistenceKind::ChannelMap, Some(instance))
483    }
484
485    pub(crate) fn channel_map_keys<T>(
486        &self,
487        channel: &ChannelMap<T>,
488    ) -> HandlerResult<Vec<String>> {
489        if self.method != InvocationMethod::Rpc {
490            return Err(HandlerError::new(
491                "dex_sdk::HandlerError",
492                "ChannelMap introspection requires an RPC invocation",
493            ));
494        }
495        self.registered_name(
496            channel.name(),
497            PersistenceKind::ChannelMap,
498            Some("key-check"),
499        )?;
500        let prefix = format!("{}/", channel.name());
501        let physical_keys = self
502            .channel_infos
503            .iter()
504            .filter(|(key, info)| key.starts_with(&prefix) && info.size > 0)
505            .map(|(key, _)| key.clone())
506            .collect::<HashSet<_>>();
507        sorted_instance_keys(&prefix, physical_keys)
508    }
509
510    /// Decodes the messages consumed by a satisfied Channel condition.
511    pub fn channel_results<T: Value>(&self, channel: &Channel<T>) -> HandlerResult<Vec<T>> {
512        self.channel_results_value(channel.name(), PersistenceKind::Channel, None)
513    }
514
515    /// Decodes messages consumed by one satisfied Channel-map condition.
516    pub fn channel_map_results<T: Value>(
517        &self,
518        channel: &ChannelMap<T>,
519        instance: &str,
520    ) -> HandlerResult<Vec<T>> {
521        self.channel_results_value(channel.name(), PersistenceKind::ChannelMap, Some(instance))
522    }
523
524    /// Appends one best-effort Stream message to the current Worker response stream.
525    ///
526    /// The call returns after the Worker's bounded output buffer accepts the frame. It does not
527    /// wait for Dex or the Stream Store, and a later server-side write failure is not returned to
528    /// the handler. One invocation may append any number of messages to the same Stream.
529    ///
530    /// # Errors
531    ///
532    /// Returns [`HandlerError`] for an RPC or Flow timeout Context, an unregistered Stream, a
533    /// capacity mismatch, encoding failure, cancellation, or a closed Worker response stream.
534    pub fn write_stream<T: Value>(&mut self, stream: &Stream<T>, value: T) -> HandlerResult<()> {
535        let output_emitter = self.step_output_emitter()?.clone();
536        let definition = self
537            .flow
538            .persistence
539            .get(stream.name())
540            .filter(|definition| {
541                definition.kind == PersistenceKind::Stream
542                    && definition.stream_identity == Some(stream.identity())
543            })
544            .ok_or_else(|| {
545                HandlerError::new(
546                    "dex_sdk::HandlerError",
547                    format!("Stream does not belong to Flow: {}", stream.name()),
548                )
549            })?;
550        if definition.stream_capacity_bytes != Some(stream.stream_capacity_bytes()) {
551            return Err(HandlerError::new(
552                "dex_sdk::HandlerError",
553                format!(
554                    "Stream capacity does not match its registered definition: {}",
555                    stream.name()
556                ),
557            ));
558        }
559        output_emitter.send_stream_write(StepStreamWrite {
560            stream_name: stream.name().to_string(),
561            stream_capacity_bytes: stream.stream_capacity_bytes(),
562            value: Some(value_mapper::encode_handler(&value)?),
563        })
564    }
565
566    pub(crate) fn prepare_buffered_text_stream(
567        &self,
568        stream: &Stream<String>,
569    ) -> HandlerResult<(
570        StepOutputEmitter,
571        InvocationCancellation,
572        tokio::runtime::Handle,
573    )> {
574        let output_emitter = self.step_output_emitter()?.clone();
575        let definition = self
576            .flow
577            .persistence
578            .get(stream.name())
579            .filter(|definition| {
580                definition.kind == PersistenceKind::Stream
581                    && definition.stream_identity == Some(stream.identity())
582            })
583            .ok_or_else(|| {
584                HandlerError::new(
585                    "dex_sdk::HandlerError",
586                    format!("Stream does not belong to Flow: {}", stream.name()),
587                )
588            })?;
589        if definition.stream_capacity_bytes != Some(stream.stream_capacity_bytes()) {
590            return Err(HandlerError::new(
591                "dex_sdk::HandlerError",
592                format!(
593                    "Stream capacity does not match its registered definition: {}",
594                    stream.name()
595                ),
596            ));
597        }
598        let runtime = self.runtime.clone().ok_or_else(|| {
599            HandlerError::new(
600                "dex_sdk::HandlerError",
601                "Buffered Stream runtime is unavailable",
602            )
603        })?;
604        Ok((output_emitter, self.cancellation.clone(), runtime))
605    }
606
607    pub(crate) fn register_step_output_finalizer(
608        &mut self,
609        finalizer: Arc<dyn StepOutputFinalizer>,
610    ) -> HandlerResult<()> {
611        if self.step_outputs_finalized {
612            return Err(HandlerError::new(
613                "dex_sdk::HandlerError",
614                "Buffered Stream invocation has finished",
615            ));
616        }
617        self.step_output_finalizers.push(finalizer);
618        Ok(())
619    }
620
621    pub(crate) fn finalize_step_outputs<T>(
622        &mut self,
623        result: HandlerResult<T>,
624    ) -> HandlerResult<T> {
625        if self.step_outputs_finalized {
626            return result;
627        }
628        self.step_outputs_finalized = true;
629        let mut result = result;
630        for finalizer in &self.step_output_finalizers {
631            if let Err(finalization_failure) = finalizer.finalize_step_output() {
632                match &mut result {
633                    Ok(_) => result = Err(finalization_failure),
634                    Err(handler_failure) => {
635                        handler_failure.attach_finalizer_error(&finalization_failure);
636                    }
637                }
638            }
639        }
640        result
641    }
642
643    pub(crate) fn cancellation(&self) -> InvocationCancellation {
644        self.cancellation.clone()
645    }
646
647    pub(crate) fn take_outputs(
648        self,
649    ) -> (Vec<AttributeWrite>, Vec<Kv>, Vec<Kv>, Vec<ChannelMessage>) {
650        (
651            self.attribute_writes.into_values().collect(),
652            self.local_writes.into_values().collect(),
653            self.events,
654            self.publications,
655        )
656    }
657
658    fn step_output_emitter(&self) -> HandlerResult<&StepOutputEmitter> {
659        if self.method == InvocationMethod::Rpc {
660            return Err(HandlerError::new(
661                "dex_sdk::HandlerError",
662                "Step progress requires a Step Context",
663            ));
664        }
665        if self.cancellation.is_cancelled() {
666            return Err(HandlerError::new(
667                "dex_sdk::HandlerError",
668                "Step invocation is canceled",
669            ));
670        }
671        self.output_emitter.as_ref().ok_or_else(|| {
672            HandlerError::new("dex_sdk::HandlerError", "Step output stream is unavailable")
673        })
674    }
675
676    fn get_attribute_value<T: Value>(
677        &self,
678        name: &str,
679        kind: PersistenceKind,
680        instance: Option<&str>,
681    ) -> HandlerResult<Option<T>> {
682        let key = self.registered_name(name, kind, instance)?;
683        if let Some(write) = self.attribute_writes.get(&key) {
684            let value = write.value.as_ref().ok_or_else(|| {
685                HandlerError::new(
686                    "dex_sdk::HandlerError",
687                    format!("Attribute write {key} has no Value"),
688                )
689            })?;
690            if matches!(value.kind, Some(value::Kind::NullValue(_))) {
691                return Ok(None);
692            }
693            return value_mapper::decode_handler(value).map(Some);
694        }
695        self.attributes
696            .get(&key)
697            .map(value_mapper::decode_handler)
698            .transpose()
699    }
700
701    fn set_attribute_value(
702        &mut self,
703        name: &str,
704        kind: PersistenceKind,
705        instance: Option<&str>,
706        value: ProtoValue,
707        index_config: Option<dex_protocol::dex::IndexConfig>,
708        sync_config: Option<dex_protocol::dex::AttributeSyncConfig>,
709    ) -> HandlerResult<()> {
710        let key = self.registered_name(name, kind, instance)?;
711        self.attribute_writes.insert(
712            key.clone(),
713            AttributeWrite {
714                key,
715                value: Some(value),
716                index_config,
717                sync_config,
718            },
719        );
720        Ok(())
721    }
722
723    fn publish_value(
724        &mut self,
725        name: &str,
726        kind: PersistenceKind,
727        instance: Option<&str>,
728        value: ProtoValue,
729    ) -> HandlerResult<()> {
730        let channel_name = self.registered_name(name, kind, instance)?;
731        self.publications.push(ChannelMessage {
732            channel_name: channel_name.clone(),
733            value: Some(value),
734        });
735        if self.method == InvocationMethod::Rpc {
736            self.channel_infos
737                .entry(channel_name)
738                .and_modify(|info| info.size += 1)
739                .or_insert(ChannelInfo { size: 1 });
740        }
741        Ok(())
742    }
743
744    fn channel_size_value(
745        &self,
746        name: &str,
747        kind: PersistenceKind,
748        instance: Option<&str>,
749    ) -> HandlerResult<usize> {
750        let channel_name = self.registered_name(name, kind, instance)?;
751        Ok(self
752            .channel_infos
753            .get(&channel_name)
754            .map_or(0, |info| usize::try_from(info.size).unwrap_or_default()))
755    }
756
757    fn channel_results_value<T: Value>(
758        &self,
759        name: &str,
760        kind: PersistenceKind,
761        instance: Option<&str>,
762    ) -> HandlerResult<Vec<T>> {
763        let channel_name = self.registered_name(name, kind, instance)?;
764        let Some(results) = &self.condition_results else {
765            return Ok(Vec::new());
766        };
767        let mut decoded = Vec::new();
768        for result in &results.channel_results {
769            if result.channel_name == channel_name
770                && result.condition_status == ConditionStatus::Completed as i32
771            {
772                for value in &result.values {
773                    decoded.push(value_mapper::decode_handler(value)?);
774                }
775            }
776        }
777        Ok(decoded)
778    }
779
780    fn registered_name(
781        &self,
782        name: &str,
783        kind: PersistenceKind,
784        instance: Option<&str>,
785    ) -> HandlerResult<String> {
786        let definition = self.flow.persistence.get(name).ok_or_else(|| {
787            HandlerError::new(
788                "dex_sdk::HandlerError",
789                format!("persistence definition does not belong to Flow: {name}"),
790            )
791        })?;
792        if definition.kind != kind {
793            return Err(HandlerError::new(
794                "dex_sdk::HandlerError",
795                format!("persistence definition kind does not match: {name}"),
796            ));
797        }
798        match kind {
799            PersistenceKind::AttributeMap | PersistenceKind::ChannelMap => instance
800                .map(|instance| physical_name(name, instance))
801                .ok_or_else(|| {
802                    HandlerError::new(
803                        "dex_sdk::HandlerError",
804                        format!("{name} requires an instance"),
805                    )
806                }),
807            PersistenceKind::Attribute | PersistenceKind::Channel if instance.is_none() => {
808                Ok(name.to_string())
809            }
810            _ => Err(HandlerError::new(
811                "dex_sdk::HandlerError",
812                format!("static persistence definition cannot use an instance: {name}"),
813            )),
814        }
815    }
816}
817
818fn sorted_instance_keys(
819    prefix: &str,
820    physical_keys: HashSet<String>,
821) -> HandlerResult<Vec<String>> {
822    let mut keys = physical_keys
823        .iter()
824        .map(|key| {
825            decode_instance(key, prefix)
826                .map_err(|error| HandlerError::new("dex_sdk::HandlerError", error))
827        })
828        .collect::<HandlerResult<Vec<_>>>()?;
829    keys.sort();
830    Ok(keys)
831}
832
833#[derive(Clone)]
834pub(crate) struct InvocationCancellation {
835    inner: Arc<InvocationCancellationInner>,
836}
837
838struct InvocationCancellationInner {
839    cancelled: AtomicBool,
840    mutex: Mutex<()>,
841    condition: Condvar,
842    notification: tokio::sync::Notify,
843}
844
845impl InvocationCancellation {
846    pub(crate) fn new() -> Self {
847        Self {
848            inner: Arc::new(InvocationCancellationInner {
849                cancelled: AtomicBool::new(false),
850                mutex: Mutex::new(()),
851                condition: Condvar::new(),
852                notification: tokio::sync::Notify::new(),
853            }),
854        }
855    }
856
857    fn wait(&self) {
858        let mut guard = self.inner.mutex.lock().expect("cancellation lock");
859        while !self.is_cancelled() {
860            guard = self.inner.condition.wait(guard).expect("cancellation lock");
861        }
862    }
863
864    pub(crate) fn is_cancelled(&self) -> bool {
865        self.inner.cancelled.load(Ordering::Acquire)
866    }
867
868    pub(crate) fn cancel(&self) {
869        let _guard = self.inner.mutex.lock().expect("cancellation lock");
870        self.inner.cancelled.store(true, Ordering::Release);
871        self.inner.condition.notify_all();
872        self.inner.notification.notify_waiters();
873    }
874
875    pub(crate) async fn cancelled(&self) {
876        let notified = self.inner.notification.notified();
877        if self.is_cancelled() {
878            return;
879        }
880        notified.await;
881    }
882}
883
884fn system_time(timestamp: i64) -> SystemTime {
885    if timestamp >= 0 {
886        SystemTime::UNIX_EPOCH + Duration::from_secs(timestamp as u64)
887    } else {
888        SystemTime::UNIX_EPOCH - Duration::from_secs(timestamp.unsigned_abs())
889    }
890}
891
892fn map_values(kind: &str, entries: Vec<Kv>) -> HandlerResult<HashMap<String, ProtoValue>> {
893    let mut mapped = HashMap::new();
894    for entry in entries {
895        require_name(&entry.key, kind)?;
896        let value = entry.value.ok_or_else(|| {
897            HandlerError::new(
898                "dex_sdk::HandlerError",
899                format!("{kind} {} has no Value", entry.key),
900            )
901        })?;
902        if mapped.insert(entry.key.clone(), value).is_some() {
903            return Err(HandlerError::new(
904                "dex_sdk::HandlerError",
905                format!("duplicate {kind} {}", entry.key),
906            ));
907        }
908    }
909    Ok(mapped)
910}
911
912fn require_name(value: &str, kind: &str) -> HandlerResult<()> {
913    if value.is_empty() {
914        Err(HandlerError::new(
915            "dex_sdk::HandlerError",
916            format!("{kind} is required"),
917        ))
918    } else {
919        Ok(())
920    }
921}
922
923#[cfg(test)]
924mod tests {
925    use super::{Context, ContextInput, InvocationCancellation, InvocationMethod};
926    use crate::{
927        AttributeMap, ChannelMap, Flow, PersistenceSchema, Registry, registry::physical_name,
928        value_mapper,
929    };
930    use dex_protocol::dex::{ChannelInfo, Context as ProtoContext, Kv};
931    use std::collections::HashMap;
932
933    struct MapFlow {
934        attributes: AttributeMap<String>,
935        channels: ChannelMap<String>,
936    }
937
938    impl Flow for MapFlow {
939        type StartInput = ();
940
941        fn persistence(&self) -> PersistenceSchema {
942            PersistenceSchema::new()
943                .attribute_map(&self.attributes)
944                .channel_map(&self.channels)
945        }
946    }
947
948    #[test]
949    fn map_introspection_tracks_buffered_changes() {
950        let flow = MapFlow {
951            attributes: AttributeMap::new("items"),
952            channels: ChannelMap::new("messages"),
953        };
954        let registry = Registry::new().register(flow).expect("register map Flow");
955        let registered = registry.flow("MapFlow").expect("lookup map Flow").clone();
956        let attributes = AttributeMap::<String>::new("items");
957        let channels = ChannelMap::<String>::new("messages");
958        let special = "special / key";
959        let mut context = Context::new(
960            ContextInput {
961                method: InvocationMethod::Rpc,
962                flow: registered,
963                metadata: ProtoContext::default(),
964                attributes: vec![
965                    Kv {
966                        key: physical_name("items", special),
967                        value: Some(value_mapper::encode_handler(&"initial".to_string()).unwrap()),
968                    },
969                    Kv {
970                        key: physical_name("items", "z"),
971                        value: Some(value_mapper::encode_handler(&"remove".to_string()).unwrap()),
972                    },
973                ],
974                locals: Vec::new(),
975                condition_results: None,
976                channel_infos: HashMap::from([
977                    (physical_name("messages", special), ChannelInfo { size: 1 }),
978                    (physical_name("messages", "empty"), ChannelInfo { size: 0 }),
979                ]),
980            },
981            None,
982            InvocationCancellation::new(),
983        )
984        .expect("create RPC Context");
985
986        assert_eq!(
987            vec![special.to_string(), "z".to_string()],
988            attributes.all_instance_keys(&context).unwrap()
989        );
990        attributes
991            .set(&mut context, "a", "added".to_string())
992            .unwrap();
993        attributes.delete(&mut context, "z").unwrap();
994        assert_eq!(
995            vec!["a".to_string(), special.to_string()],
996            attributes.all_instance_keys(&context).unwrap()
997        );
998        assert_eq!(2, attributes.map_size(&context).unwrap());
999
1000        assert_eq!(
1001            vec![special.to_string()],
1002            channels.all_instance_keys(&context).unwrap()
1003        );
1004        channels
1005            .publish(&mut context, "a", "published".to_string())
1006            .unwrap();
1007        assert_eq!(
1008            vec!["a".to_string(), special.to_string()],
1009            channels.all_instance_keys(&context).unwrap()
1010        );
1011        assert_eq!(2, channels.map_size(&context).unwrap());
1012        assert!(context.record_heartbeat().is_err());
1013        assert!(
1014            context
1015                .record_heartbeat_value("checkpoint".to_string())
1016                .is_err()
1017        );
1018        assert!(context.last_heartbeat_value::<String>().is_err());
1019    }
1020}