1use 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
45pub 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 pub fn flow_id(&self) -> &str {
103 &self.metadata.flow_id
104 }
105
106 pub fn run_id(&self) -> &str {
108 &self.metadata.run_id
109 }
110
111 pub fn flow_started_at(&self) -> SystemTime {
113 system_time(self.metadata.flow_started_timestamp)
114 }
115
116 pub fn step_execution_id(&self) -> &str {
118 &self.metadata.step_execution_id
119 }
120
121 pub fn from_step_execution_id(&self) -> &str {
123 &self.metadata.from_step_execution_id
124 }
125
126 pub fn first_attempt_at(&self) -> SystemTime {
128 system_time(self.metadata.first_attempt_timestamp)
129 }
130
131 pub fn attempt(&self) -> u32 {
133 u32::try_from(self.metadata.attempt).unwrap_or_default()
134 }
135
136 pub fn record_heartbeat(&mut self) -> HandlerResult<()> {
146 self.step_output_emitter()?.send_heartbeat(None)
147 }
148
149 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 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 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 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 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 pub fn wait_for_cancellation(&self) {
266 self.cancellation.wait();
267 }
268
269 pub fn is_cancelled(&self) -> bool {
271 self.cancellation.is_cancelled()
272 }
273
274 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 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 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 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 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 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 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 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 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 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 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 pub fn channel_size<T>(&self, channel: &Channel<T>) -> HandlerResult<usize> {
473 self.channel_size_value(channel.name(), PersistenceKind::Channel, None)
474 }
475
476 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 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 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 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}