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, ChannelMessageDeletion, ConditionResults,
16 ConditionStatus, 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(crate) struct InvocationOutputs {
46 pub(crate) attributes: Vec<AttributeWrite>,
47 pub(crate) locals: Vec<Kv>,
48 pub(crate) events: Vec<Kv>,
49 pub(crate) publications: Vec<ChannelMessage>,
50 pub(crate) channel_deletions: Vec<ChannelMessageDeletion>,
51}
52
53pub struct Context {
59 method: InvocationMethod,
60 flow: RegisteredFlow,
61 output_emitter: Option<StepOutputEmitter>,
62 metadata: ProtoContext,
63 attributes: HashMap<String, ProtoValue>,
64 locals: HashMap<String, ProtoValue>,
65 condition_results: Option<ConditionResults>,
66 channel_infos: HashMap<String, ChannelInfo>,
67 attribute_writes: HashMap<String, AttributeWrite>,
68 local_writes: HashMap<String, Kv>,
69 events: Vec<Kv>,
70 event_names: HashSet<String>,
71 publications: Vec<ChannelMessage>,
72 channel_deletions: Vec<ChannelMessageDeletion>,
73 cancellation: InvocationCancellation,
74 runtime: Option<tokio::runtime::Handle>,
75 step_output_finalizers: Vec<Arc<dyn StepOutputFinalizer>>,
76 step_outputs_finalized: bool,
77}
78
79pub(crate) trait StepOutputFinalizer: Send + Sync {
80 fn finalize_step_output(&self) -> HandlerResult<()>;
81}
82
83impl Context {
84 pub(crate) fn new(
85 input: ContextInput,
86 output_emitter: Option<StepOutputEmitter>,
87 cancellation: InvocationCancellation,
88 ) -> HandlerResult<Self> {
89 Ok(Self {
90 method: input.method,
91 flow: input.flow,
92 output_emitter,
93 metadata: input.metadata,
94 attributes: map_values("Attribute", input.attributes)?,
95 locals: map_values("step-execution local", input.locals)?,
96 condition_results: input.condition_results,
97 channel_infos: input.channel_infos,
98 attribute_writes: HashMap::new(),
99 local_writes: HashMap::new(),
100 events: Vec::new(),
101 event_names: HashSet::new(),
102 publications: Vec::new(),
103 channel_deletions: Vec::new(),
104 cancellation,
105 runtime: tokio::runtime::Handle::try_current().ok(),
106 step_output_finalizers: Vec::new(),
107 step_outputs_finalized: false,
108 })
109 }
110
111 pub fn flow_id(&self) -> &str {
113 &self.metadata.flow_id
114 }
115
116 pub fn run_id(&self) -> &str {
118 &self.metadata.run_id
119 }
120
121 pub fn flow_started_at(&self) -> SystemTime {
123 system_time(self.metadata.flow_started_timestamp)
124 }
125
126 pub fn step_execution_id(&self) -> &str {
128 &self.metadata.step_execution_id
129 }
130
131 pub fn from_step_execution_id(&self) -> &str {
133 &self.metadata.from_step_execution_id
134 }
135
136 pub fn first_attempt_at(&self) -> SystemTime {
138 system_time(self.metadata.first_attempt_timestamp)
139 }
140
141 pub fn attempt(&self) -> u32 {
143 u32::try_from(self.metadata.attempt).unwrap_or_default()
144 }
145
146 pub fn record_heartbeat(&mut self) -> HandlerResult<()> {
156 self.step_output_emitter()?.send_heartbeat(None)
157 }
158
159 pub fn record_heartbeat_value<T: Value>(&mut self, value: T) -> HandlerResult<()> {
188 let value = value_mapper::encode_handler(&value)?;
189 self.step_output_emitter()?.send_heartbeat(Some(value))
190 }
191
192 pub fn last_heartbeat_value<T: Value>(&self) -> HandlerResult<Option<T>> {
203 if self.method == InvocationMethod::Rpc {
204 return Err(HandlerError::new(
205 "dex_sdk::HandlerError",
206 "Heartbeat values require a Step Context",
207 ));
208 }
209 self.metadata
210 .last_heartbeat_value
211 .as_ref()
212 .map(value_mapper::decode_handler)
213 .transpose()
214 }
215
216 pub fn has_any_timer_fired(&self) -> bool {
218 self.condition_results.as_ref().is_some_and(|results| {
219 results
220 .timer_results
221 .iter()
222 .any(|timer| timer.condition_status == ConditionStatus::Completed as i32)
223 })
224 }
225
226 pub fn has_timer_fired(&self, index: usize) -> bool {
230 self.condition_results
231 .as_ref()
232 .and_then(|results| results.timer_results.get(index))
233 .is_some_and(|timer| timer.condition_status == ConditionStatus::Completed as i32)
234 }
235
236 pub fn wait_for_method_failed(&self) -> bool {
238 self.condition_results
239 .as_ref()
240 .is_some_and(|results| results.wait_for_failed)
241 }
242
243 pub(crate) fn sub_flow_result(&self, index: usize) -> HandlerResult<FlowResult> {
244 if self.method != InvocationMethod::Execute {
245 return Err(HandlerError::new(
246 "dex_sdk::HandlerError",
247 "SubFlow results are available only during execute",
248 ));
249 }
250 let result = self
251 .condition_results
252 .as_ref()
253 .and_then(|results| results.sub_flow_results.get(index))
254 .ok_or_else(|| {
255 HandlerError::new(
256 "dex_sdk::HandlerError",
257 format!("SubFlow result index is unavailable: {index}"),
258 )
259 })?;
260 FlowResult::from_proto(result).map_err(HandlerError::from_error)
261 }
262
263 pub(crate) fn sub_flow_id(&self, index: usize) -> HandlerResult<String> {
264 self.sub_flow_result(index)?;
265 Ok(format!(
266 "SubFlow:{}-{}-{index}",
267 self.flow_id(),
268 self.step_execution_id()
269 ))
270 }
271
272 pub fn wait_for_cancellation(&self) {
276 self.cancellation.wait();
277 }
278
279 pub fn is_cancelled(&self) -> bool {
281 self.cancellation.is_cancelled()
282 }
283
284 pub fn set_step_execution_local<T: Value>(&mut self, key: &str, value: T) -> HandlerResult<()> {
290 require_name(key, "step-execution local key")?;
291 let value = value_mapper::encode_handler(&value)?;
292 self.local_writes.insert(
293 key.to_string(),
294 Kv {
295 key: key.to_string(),
296 value: Some(value),
297 },
298 );
299 Ok(())
300 }
301
302 pub fn step_execution_local<T: Value>(&self, key: &str) -> HandlerResult<T> {
308 require_name(key, "step-execution local key")?;
309 let value = self
310 .local_writes
311 .get(key)
312 .and_then(|entry| entry.value.as_ref())
313 .or_else(|| self.locals.get(key))
314 .ok_or_else(|| {
315 HandlerError::new(
316 "dex_sdk::HandlerError",
317 format!("step-execution local {key} is missing"),
318 )
319 })?;
320 value_mapper::decode_handler(value)
321 }
322
323 pub fn record_event<T: Value>(&mut self, name: &str, value: T) -> HandlerResult<()> {
331 require_name(name, "event name")?;
332 if !self.event_names.insert(name.to_string()) {
333 return Err(HandlerError::new(
334 "dex_sdk::HandlerError",
335 format!("event was already recorded: {name}"),
336 ));
337 }
338 self.events.push(Kv {
339 key: name.to_string(),
340 value: Some(value_mapper::encode_handler(&value)?),
341 });
342 Ok(())
343 }
344
345 pub fn get_attribute<T: Value>(&self, attribute: &Attribute<T>) -> HandlerResult<Option<T>> {
347 self.get_attribute_value(attribute.name(), PersistenceKind::Attribute, None)
348 }
349
350 pub fn get_attribute_map<T: Value>(
352 &self,
353 attribute: &AttributeMap<T>,
354 instance: &str,
355 ) -> HandlerResult<Option<T>> {
356 self.get_attribute_value(
357 attribute.name(),
358 PersistenceKind::AttributeMap,
359 Some(instance),
360 )
361 }
362
363 pub fn set_attribute<T: Value>(
365 &mut self,
366 attribute: &Attribute<T>,
367 value: T,
368 ) -> HandlerResult<()> {
369 self.set_attribute_value(
370 attribute.name(),
371 PersistenceKind::Attribute,
372 None,
373 value_mapper::encode_handler(&value)?,
374 attribute.index().map(|index| index.proto_config(false)),
375 attribute.sync_config(),
376 )
377 }
378
379 pub fn set_attribute_map<T: Value>(
381 &mut self,
382 attribute: &AttributeMap<T>,
383 instance: &str,
384 value: T,
385 ) -> HandlerResult<()> {
386 self.set_attribute_value(
387 attribute.name(),
388 PersistenceKind::AttributeMap,
389 Some(instance),
390 value_mapper::encode_handler(&value)?,
391 attribute.index().map(|index| index.proto_config(true)),
392 attribute.sync_config(),
393 )
394 }
395
396 pub fn delete_attribute<T>(&mut self, attribute: &Attribute<T>) -> HandlerResult<()> {
398 self.set_attribute_value(
399 attribute.name(),
400 PersistenceKind::Attribute,
401 None,
402 value_mapper::deletion(),
403 attribute.index().map(|index| index.proto_config(false)),
404 attribute.sync_config(),
405 )
406 }
407
408 pub fn delete_attribute_map<T>(
410 &mut self,
411 attribute: &AttributeMap<T>,
412 instance: &str,
413 ) -> HandlerResult<()> {
414 self.set_attribute_value(
415 attribute.name(),
416 PersistenceKind::AttributeMap,
417 Some(instance),
418 value_mapper::deletion(),
419 attribute.index().map(|index| index.proto_config(true)),
420 attribute.sync_config(),
421 )
422 }
423
424 pub(crate) fn attribute_map_keys<T>(
425 &self,
426 attribute: &AttributeMap<T>,
427 ) -> HandlerResult<Vec<String>> {
428 self.registered_name(
429 attribute.name(),
430 PersistenceKind::AttributeMap,
431 Some("key-check"),
432 )?;
433 let prefix = format!("{}/", attribute.name());
434 let mut physical_keys = self
435 .attributes
436 .keys()
437 .filter(|key| key.starts_with(&prefix))
438 .cloned()
439 .collect::<HashSet<_>>();
440 for (key, write) in &self.attribute_writes {
441 if !key.starts_with(&prefix) {
442 continue;
443 }
444 if matches!(
445 write.value.as_ref().and_then(|value| value.kind.as_ref()),
446 Some(value::Kind::NullValue(_))
447 ) {
448 physical_keys.remove(key);
449 } else {
450 physical_keys.insert(key.clone());
451 }
452 }
453 sorted_instance_keys(&prefix, physical_keys)
454 }
455
456 pub fn publish<T: Value>(&mut self, channel: &Channel<T>, value: T) -> HandlerResult<()> {
458 self.publish_value(
459 channel.name(),
460 PersistenceKind::Channel,
461 None,
462 value_mapper::encode_handler(&value)?,
463 )
464 }
465
466 pub fn publish_map<T: Value>(
468 &mut self,
469 channel: &ChannelMap<T>,
470 instance: &str,
471 value: T,
472 ) -> HandlerResult<()> {
473 self.publish_value(
474 channel.name(),
475 PersistenceKind::ChannelMap,
476 Some(instance),
477 value_mapper::encode_handler(&value)?,
478 )
479 }
480
481 pub fn channel_size<T>(&self, channel: &Channel<T>) -> HandlerResult<usize> {
483 self.channel_size_value(channel.name(), PersistenceKind::Channel, None)
484 }
485
486 pub fn channel_map_size<T>(
488 &self,
489 channel: &ChannelMap<T>,
490 instance: &str,
491 ) -> HandlerResult<usize> {
492 self.channel_size_value(channel.name(), PersistenceKind::ChannelMap, Some(instance))
493 }
494
495 pub(crate) fn channel_map_keys<T>(
496 &self,
497 channel: &ChannelMap<T>,
498 ) -> HandlerResult<Vec<String>> {
499 if self.method != InvocationMethod::Rpc {
500 return Err(HandlerError::new(
501 "dex_sdk::HandlerError",
502 "ChannelMap introspection requires an RPC invocation",
503 ));
504 }
505 self.registered_name(
506 channel.name(),
507 PersistenceKind::ChannelMap,
508 Some("key-check"),
509 )?;
510 let prefix = format!("{}/", channel.name());
511 let physical_keys = self
512 .channel_infos
513 .iter()
514 .filter(|(key, info)| key.starts_with(&prefix) && info.size > 0)
515 .map(|(key, _)| key.clone())
516 .collect::<HashSet<_>>();
517 sorted_instance_keys(&prefix, physical_keys)
518 }
519
520 pub fn channel_results<T: Value>(&self, channel: &Channel<T>) -> HandlerResult<Vec<T>> {
522 self.channel_results_value(channel.name(), PersistenceKind::Channel, None)
523 }
524
525 pub fn channel_map_results<T: Value>(
527 &self,
528 channel: &ChannelMap<T>,
529 instance: &str,
530 ) -> HandlerResult<Vec<T>> {
531 self.channel_results_value(channel.name(), PersistenceKind::ChannelMap, Some(instance))
532 }
533
534 pub fn write_stream<T: Value>(&mut self, stream: &Stream<T>, value: T) -> HandlerResult<()> {
545 let output_emitter = self.step_output_emitter()?.clone();
546 let definition = self
547 .flow
548 .persistence
549 .get(stream.name())
550 .filter(|definition| {
551 definition.kind == PersistenceKind::Stream
552 && definition.stream_identity == Some(stream.identity())
553 })
554 .ok_or_else(|| {
555 HandlerError::new(
556 "dex_sdk::HandlerError",
557 format!("Stream does not belong to Flow: {}", stream.name()),
558 )
559 })?;
560 if definition.stream_capacity_bytes != Some(stream.stream_capacity_bytes()) {
561 return Err(HandlerError::new(
562 "dex_sdk::HandlerError",
563 format!(
564 "Stream capacity does not match its registered definition: {}",
565 stream.name()
566 ),
567 ));
568 }
569 output_emitter.send_stream_write(StepStreamWrite {
570 stream_name: stream.name().to_string(),
571 stream_capacity_bytes: stream.stream_capacity_bytes(),
572 value: Some(value_mapper::encode_handler(&value)?),
573 })
574 }
575
576 pub(crate) fn prepare_buffered_text_stream(
577 &self,
578 stream: &Stream<String>,
579 ) -> HandlerResult<(
580 StepOutputEmitter,
581 InvocationCancellation,
582 tokio::runtime::Handle,
583 )> {
584 let output_emitter = self.step_output_emitter()?.clone();
585 let definition = self
586 .flow
587 .persistence
588 .get(stream.name())
589 .filter(|definition| {
590 definition.kind == PersistenceKind::Stream
591 && definition.stream_identity == Some(stream.identity())
592 })
593 .ok_or_else(|| {
594 HandlerError::new(
595 "dex_sdk::HandlerError",
596 format!("Stream does not belong to Flow: {}", stream.name()),
597 )
598 })?;
599 if definition.stream_capacity_bytes != Some(stream.stream_capacity_bytes()) {
600 return Err(HandlerError::new(
601 "dex_sdk::HandlerError",
602 format!(
603 "Stream capacity does not match its registered definition: {}",
604 stream.name()
605 ),
606 ));
607 }
608 let runtime = self.runtime.clone().ok_or_else(|| {
609 HandlerError::new(
610 "dex_sdk::HandlerError",
611 "Buffered Stream runtime is unavailable",
612 )
613 })?;
614 Ok((output_emitter, self.cancellation.clone(), runtime))
615 }
616
617 pub(crate) fn register_step_output_finalizer(
618 &mut self,
619 finalizer: Arc<dyn StepOutputFinalizer>,
620 ) -> HandlerResult<()> {
621 if self.step_outputs_finalized {
622 return Err(HandlerError::new(
623 "dex_sdk::HandlerError",
624 "Buffered Stream invocation has finished",
625 ));
626 }
627 self.step_output_finalizers.push(finalizer);
628 Ok(())
629 }
630
631 pub(crate) fn finalize_step_outputs<T>(
632 &mut self,
633 result: HandlerResult<T>,
634 ) -> HandlerResult<T> {
635 if self.step_outputs_finalized {
636 return result;
637 }
638 self.step_outputs_finalized = true;
639 let mut result = result;
640 for finalizer in &self.step_output_finalizers {
641 if let Err(finalization_failure) = finalizer.finalize_step_output() {
642 match &mut result {
643 Ok(_) => result = Err(finalization_failure),
644 Err(handler_failure) => {
645 handler_failure.attach_finalizer_error(&finalization_failure);
646 }
647 }
648 }
649 }
650 result
651 }
652
653 pub(crate) fn cancellation(&self) -> InvocationCancellation {
654 self.cancellation.clone()
655 }
656
657 pub(crate) fn take_outputs(self) -> InvocationOutputs {
658 InvocationOutputs {
659 attributes: self.attribute_writes.into_values().collect(),
660 locals: self.local_writes.into_values().collect(),
661 events: self.events,
662 publications: self.publications,
663 channel_deletions: self.channel_deletions,
664 }
665 }
666
667 fn step_output_emitter(&self) -> HandlerResult<&StepOutputEmitter> {
668 if self.method == InvocationMethod::Rpc {
669 return Err(HandlerError::new(
670 "dex_sdk::HandlerError",
671 "Step progress requires a Step Context",
672 ));
673 }
674 if self.cancellation.is_cancelled() {
675 return Err(HandlerError::new(
676 "dex_sdk::HandlerError",
677 "Step invocation is canceled",
678 ));
679 }
680 self.output_emitter.as_ref().ok_or_else(|| {
681 HandlerError::new("dex_sdk::HandlerError", "Step output stream is unavailable")
682 })
683 }
684
685 fn get_attribute_value<T: Value>(
686 &self,
687 name: &str,
688 kind: PersistenceKind,
689 instance: Option<&str>,
690 ) -> HandlerResult<Option<T>> {
691 let key = self.registered_name(name, kind, instance)?;
692 if let Some(write) = self.attribute_writes.get(&key) {
693 let value = write.value.as_ref().ok_or_else(|| {
694 HandlerError::new(
695 "dex_sdk::HandlerError",
696 format!("Attribute write {key} has no Value"),
697 )
698 })?;
699 if matches!(value.kind, Some(value::Kind::NullValue(_))) {
700 return Ok(None);
701 }
702 return value_mapper::decode_handler(value).map(Some);
703 }
704 self.attributes
705 .get(&key)
706 .map(value_mapper::decode_handler)
707 .transpose()
708 }
709
710 fn set_attribute_value(
711 &mut self,
712 name: &str,
713 kind: PersistenceKind,
714 instance: Option<&str>,
715 value: ProtoValue,
716 index_config: Option<dex_protocol::dex::IndexConfig>,
717 sync_config: Option<dex_protocol::dex::AttributeSyncConfig>,
718 ) -> HandlerResult<()> {
719 let key = self.registered_name(name, kind, instance)?;
720 self.attribute_writes.insert(
721 key.clone(),
722 AttributeWrite {
723 key,
724 value: Some(value),
725 index_config,
726 sync_config,
727 },
728 );
729 Ok(())
730 }
731
732 fn publish_value(
733 &mut self,
734 name: &str,
735 kind: PersistenceKind,
736 instance: Option<&str>,
737 value: ProtoValue,
738 ) -> HandlerResult<()> {
739 let channel_name = self.registered_name(name, kind, instance)?;
740 self.publications.push(ChannelMessage {
741 channel_name: channel_name.clone(),
742 value: Some(value),
743 message_id: String::new(),
744 });
745 if self.method == InvocationMethod::Rpc {
746 self.channel_infos
747 .entry(channel_name)
748 .and_modify(|info| info.size += 1)
749 .or_insert(ChannelInfo { size: 1 });
750 }
751 Ok(())
752 }
753
754 pub fn delete_channel_message<T>(
756 &mut self,
757 channel: &Channel<T>,
758 message_id: &str,
759 ) -> HandlerResult<()> {
760 self.delete_channel_message_value(
761 channel.name(),
762 PersistenceKind::Channel,
763 None,
764 message_id,
765 )
766 }
767
768 pub fn delete_channel_map_message<T>(
770 &mut self,
771 channel: &ChannelMap<T>,
772 instance: &str,
773 message_id: &str,
774 ) -> HandlerResult<()> {
775 self.delete_channel_message_value(
776 channel.name(),
777 PersistenceKind::ChannelMap,
778 Some(instance),
779 message_id,
780 )
781 }
782
783 fn delete_channel_message_value(
784 &mut self,
785 name: &str,
786 kind: PersistenceKind,
787 instance: Option<&str>,
788 message_id: &str,
789 ) -> HandlerResult<()> {
790 if self.method != InvocationMethod::Rpc {
791 return Err(HandlerError::new(
792 "dex_sdk::HandlerError",
793 "Channel message deletion requires an RPC Context",
794 ));
795 }
796 if message_id.is_empty() {
797 return Err(HandlerError::new(
798 "dex_sdk::HandlerError",
799 "Channel message ID must not be empty",
800 ));
801 }
802 let channel_name = self.registered_name(name, kind, instance)?;
803 self.channel_deletions.push(ChannelMessageDeletion {
804 channel_name: channel_name.clone(),
805 message_id: message_id.to_string(),
806 });
807 if let Some(info) = self.channel_infos.get_mut(&channel_name) {
808 info.size = info.size.saturating_sub(1);
809 }
810 Ok(())
811 }
812
813 fn channel_size_value(
814 &self,
815 name: &str,
816 kind: PersistenceKind,
817 instance: Option<&str>,
818 ) -> HandlerResult<usize> {
819 let channel_name = self.registered_name(name, kind, instance)?;
820 Ok(self
821 .channel_infos
822 .get(&channel_name)
823 .map_or(0, |info| usize::try_from(info.size).unwrap_or_default()))
824 }
825
826 fn channel_results_value<T: Value>(
827 &self,
828 name: &str,
829 kind: PersistenceKind,
830 instance: Option<&str>,
831 ) -> HandlerResult<Vec<T>> {
832 let channel_name = self.registered_name(name, kind, instance)?;
833 let Some(results) = &self.condition_results else {
834 return Ok(Vec::new());
835 };
836 let mut decoded = Vec::new();
837 for result in &results.channel_results {
838 if result.channel_name == channel_name
839 && result.condition_status == ConditionStatus::Completed as i32
840 {
841 for value in &result.values {
842 decoded.push(value_mapper::decode_handler(value)?);
843 }
844 }
845 }
846 Ok(decoded)
847 }
848
849 fn registered_name(
850 &self,
851 name: &str,
852 kind: PersistenceKind,
853 instance: Option<&str>,
854 ) -> HandlerResult<String> {
855 let definition = self.flow.persistence.get(name).ok_or_else(|| {
856 HandlerError::new(
857 "dex_sdk::HandlerError",
858 format!("persistence definition does not belong to Flow: {name}"),
859 )
860 })?;
861 if definition.kind != kind {
862 return Err(HandlerError::new(
863 "dex_sdk::HandlerError",
864 format!("persistence definition kind does not match: {name}"),
865 ));
866 }
867 match kind {
868 PersistenceKind::AttributeMap | PersistenceKind::ChannelMap => instance
869 .map(|instance| physical_name(name, instance))
870 .ok_or_else(|| {
871 HandlerError::new(
872 "dex_sdk::HandlerError",
873 format!("{name} requires an instance"),
874 )
875 }),
876 PersistenceKind::Attribute | PersistenceKind::Channel if instance.is_none() => {
877 Ok(name.to_string())
878 }
879 _ => Err(HandlerError::new(
880 "dex_sdk::HandlerError",
881 format!("static persistence definition cannot use an instance: {name}"),
882 )),
883 }
884 }
885}
886
887fn sorted_instance_keys(
888 prefix: &str,
889 physical_keys: HashSet<String>,
890) -> HandlerResult<Vec<String>> {
891 let mut keys = physical_keys
892 .iter()
893 .map(|key| {
894 decode_instance(key, prefix)
895 .map_err(|error| HandlerError::new("dex_sdk::HandlerError", error))
896 })
897 .collect::<HandlerResult<Vec<_>>>()?;
898 keys.sort();
899 Ok(keys)
900}
901
902#[derive(Clone)]
903pub(crate) struct InvocationCancellation {
904 inner: Arc<InvocationCancellationInner>,
905}
906
907struct InvocationCancellationInner {
908 cancelled: AtomicBool,
909 mutex: Mutex<()>,
910 condition: Condvar,
911 notification: tokio::sync::Notify,
912}
913
914impl InvocationCancellation {
915 pub(crate) fn new() -> Self {
916 Self {
917 inner: Arc::new(InvocationCancellationInner {
918 cancelled: AtomicBool::new(false),
919 mutex: Mutex::new(()),
920 condition: Condvar::new(),
921 notification: tokio::sync::Notify::new(),
922 }),
923 }
924 }
925
926 fn wait(&self) {
927 let mut guard = self.inner.mutex.lock().expect("cancellation lock");
928 while !self.is_cancelled() {
929 guard = self.inner.condition.wait(guard).expect("cancellation lock");
930 }
931 }
932
933 pub(crate) fn is_cancelled(&self) -> bool {
934 self.inner.cancelled.load(Ordering::Acquire)
935 }
936
937 pub(crate) fn cancel(&self) {
938 let _guard = self.inner.mutex.lock().expect("cancellation lock");
939 self.inner.cancelled.store(true, Ordering::Release);
940 self.inner.condition.notify_all();
941 self.inner.notification.notify_waiters();
942 }
943
944 pub(crate) async fn cancelled(&self) {
945 let notified = self.inner.notification.notified();
946 if self.is_cancelled() {
947 return;
948 }
949 notified.await;
950 }
951}
952
953fn system_time(timestamp: i64) -> SystemTime {
954 if timestamp >= 0 {
955 SystemTime::UNIX_EPOCH + Duration::from_secs(timestamp as u64)
956 } else {
957 SystemTime::UNIX_EPOCH - Duration::from_secs(timestamp.unsigned_abs())
958 }
959}
960
961fn map_values(kind: &str, entries: Vec<Kv>) -> HandlerResult<HashMap<String, ProtoValue>> {
962 let mut mapped = HashMap::new();
963 for entry in entries {
964 require_name(&entry.key, kind)?;
965 let value = entry.value.ok_or_else(|| {
966 HandlerError::new(
967 "dex_sdk::HandlerError",
968 format!("{kind} {} has no Value", entry.key),
969 )
970 })?;
971 if mapped.insert(entry.key.clone(), value).is_some() {
972 return Err(HandlerError::new(
973 "dex_sdk::HandlerError",
974 format!("duplicate {kind} {}", entry.key),
975 ));
976 }
977 }
978 Ok(mapped)
979}
980
981fn require_name(value: &str, kind: &str) -> HandlerResult<()> {
982 if value.is_empty() {
983 Err(HandlerError::new(
984 "dex_sdk::HandlerError",
985 format!("{kind} is required"),
986 ))
987 } else {
988 Ok(())
989 }
990}
991
992#[cfg(test)]
993mod tests {
994 use super::{Context, ContextInput, InvocationCancellation, InvocationMethod};
995 use crate::{
996 AttributeMap, ChannelMap, Flow, PersistenceSchema, Registry, registry::physical_name,
997 value_mapper,
998 };
999 use dex_protocol::dex::{ChannelInfo, Context as ProtoContext, Kv};
1000 use std::collections::HashMap;
1001
1002 struct MapFlow {
1003 attributes: AttributeMap<String>,
1004 channels: ChannelMap<String>,
1005 }
1006
1007 impl Flow for MapFlow {
1008 type StartInput = ();
1009
1010 fn persistence(&self) -> PersistenceSchema {
1011 PersistenceSchema::new()
1012 .attribute_map(&self.attributes)
1013 .channel_map(&self.channels)
1014 }
1015 }
1016
1017 #[test]
1018 fn map_introspection_tracks_buffered_changes() {
1019 let flow = MapFlow {
1020 attributes: AttributeMap::new("items"),
1021 channels: ChannelMap::new("messages"),
1022 };
1023 let registry = Registry::new().register(flow).expect("register map Flow");
1024 let registered = registry.flow("MapFlow").expect("lookup map Flow").clone();
1025 let attributes = AttributeMap::<String>::new("items");
1026 let channels = ChannelMap::<String>::new("messages");
1027 let special = "special / key";
1028 let mut context = Context::new(
1029 ContextInput {
1030 method: InvocationMethod::Rpc,
1031 flow: registered,
1032 metadata: ProtoContext::default(),
1033 attributes: vec![
1034 Kv {
1035 key: physical_name("items", special),
1036 value: Some(value_mapper::encode_handler(&"initial".to_string()).unwrap()),
1037 },
1038 Kv {
1039 key: physical_name("items", "z"),
1040 value: Some(value_mapper::encode_handler(&"remove".to_string()).unwrap()),
1041 },
1042 ],
1043 locals: Vec::new(),
1044 condition_results: None,
1045 channel_infos: HashMap::from([
1046 (physical_name("messages", special), ChannelInfo { size: 1 }),
1047 (physical_name("messages", "empty"), ChannelInfo { size: 0 }),
1048 ]),
1049 },
1050 None,
1051 InvocationCancellation::new(),
1052 )
1053 .expect("create RPC Context");
1054
1055 assert_eq!(
1056 vec![special.to_string(), "z".to_string()],
1057 attributes.all_instance_keys(&context).unwrap()
1058 );
1059 attributes
1060 .set(&mut context, "a", "added".to_string())
1061 .unwrap();
1062 attributes.delete(&mut context, "z").unwrap();
1063 assert_eq!(
1064 vec!["a".to_string(), special.to_string()],
1065 attributes.all_instance_keys(&context).unwrap()
1066 );
1067 assert_eq!(2, attributes.map_size(&context).unwrap());
1068
1069 assert_eq!(
1070 vec![special.to_string()],
1071 channels.all_instance_keys(&context).unwrap()
1072 );
1073 channels
1074 .publish(&mut context, "a", "published".to_string())
1075 .unwrap();
1076 assert_eq!(
1077 vec!["a".to_string(), special.to_string()],
1078 channels.all_instance_keys(&context).unwrap()
1079 );
1080 assert_eq!(2, channels.map_size(&context).unwrap());
1081 assert!(context.record_heartbeat().is_err());
1082 assert!(
1083 context
1084 .record_heartbeat_value("checkpoint".to_string())
1085 .is_err()
1086 );
1087 assert!(context.last_heartbeat_value::<String>().is_err());
1088 }
1089}