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, Value as ProtoValue, value,
17};
18
19use crate::persistence::PersistenceKind;
20use crate::registry::{RegisteredFlow, decode_instance, physical_name};
21use crate::value_mapper;
22use crate::{
23 Attribute, AttributeMap, Channel, ChannelMap, FlowResult, HandlerError, HandlerResult, Value,
24};
25
26#[derive(Clone, Copy, Eq, PartialEq)]
27pub(crate) enum InvocationMethod {
28 WaitFor,
29 Execute,
30 Rpc,
31}
32
33pub struct Context {
39 method: InvocationMethod,
40 flow: RegisteredFlow,
41 metadata: ProtoContext,
42 attributes: HashMap<String, ProtoValue>,
43 locals: HashMap<String, ProtoValue>,
44 condition_results: Option<ConditionResults>,
45 channel_infos: HashMap<String, ChannelInfo>,
46 attribute_writes: HashMap<String, AttributeWrite>,
47 local_writes: HashMap<String, Kv>,
48 events: Vec<Kv>,
49 event_names: HashSet<String>,
50 publications: Vec<ChannelMessage>,
51 cancellation: InvocationCancellation,
52}
53
54impl Context {
55 pub fn flow_id(&self) -> &str {
57 &self.metadata.flow_id
58 }
59
60 pub fn run_id(&self) -> &str {
62 &self.metadata.run_id
63 }
64
65 pub fn flow_started_at(&self) -> SystemTime {
67 system_time(self.metadata.flow_started_timestamp)
68 }
69
70 pub fn step_execution_id(&self) -> &str {
72 &self.metadata.step_execution_id
73 }
74
75 pub fn from_step_execution_id(&self) -> &str {
77 &self.metadata.from_step_execution_id
78 }
79
80 pub fn first_attempt_at(&self) -> SystemTime {
82 system_time(self.metadata.first_attempt_timestamp)
83 }
84
85 pub fn attempt(&self) -> u32 {
87 u32::try_from(self.metadata.attempt).unwrap_or_default()
88 }
89
90 pub fn has_any_timer_fired(&self) -> bool {
92 self.condition_results.as_ref().is_some_and(|results| {
93 results
94 .timer_results
95 .iter()
96 .any(|timer| timer.condition_status == ConditionStatus::Completed as i32)
97 })
98 }
99
100 pub fn has_timer_fired(&self, index: usize) -> bool {
104 self.condition_results
105 .as_ref()
106 .and_then(|results| results.timer_results.get(index))
107 .is_some_and(|timer| timer.condition_status == ConditionStatus::Completed as i32)
108 }
109
110 pub fn wait_for_method_failed(&self) -> bool {
112 self.condition_results
113 .as_ref()
114 .is_some_and(|results| results.wait_for_failed)
115 }
116
117 pub(crate) fn sub_flow_result(&self, index: usize) -> HandlerResult<FlowResult> {
118 if self.method != InvocationMethod::Execute {
119 return Err(HandlerError::new(
120 "dex_sdk::HandlerError",
121 "SubFlow results are available only during execute",
122 ));
123 }
124 let result = self
125 .condition_results
126 .as_ref()
127 .and_then(|results| results.sub_flow_results.get(index))
128 .ok_or_else(|| {
129 HandlerError::new(
130 "dex_sdk::HandlerError",
131 format!("SubFlow result index is unavailable: {index}"),
132 )
133 })?;
134 FlowResult::from_proto(result).map_err(HandlerError::from_error)
135 }
136
137 pub(crate) fn sub_flow_id(&self, index: usize) -> HandlerResult<String> {
138 self.sub_flow_result(index)?;
139 Ok(format!(
140 "SubFlow:{}-{}-{index}",
141 self.flow_id(),
142 self.step_execution_id()
143 ))
144 }
145
146 pub fn wait_for_cancellation(&self) {
150 self.cancellation.wait();
151 }
152
153 pub fn is_cancelled(&self) -> bool {
155 self.cancellation.is_cancelled()
156 }
157
158 pub fn set_step_execution_local<T: Value>(&mut self, key: &str, value: T) -> HandlerResult<()> {
164 require_name(key, "step-execution local key")?;
165 let value = value_mapper::encode_handler(&value)?;
166 self.local_writes.insert(
167 key.to_string(),
168 Kv {
169 key: key.to_string(),
170 value: Some(value),
171 },
172 );
173 Ok(())
174 }
175
176 pub fn step_execution_local<T: Value>(&self, key: &str) -> HandlerResult<T> {
182 require_name(key, "step-execution local key")?;
183 let value = self
184 .local_writes
185 .get(key)
186 .and_then(|entry| entry.value.as_ref())
187 .or_else(|| self.locals.get(key))
188 .ok_or_else(|| {
189 HandlerError::new(
190 "dex_sdk::HandlerError",
191 format!("step-execution local {key} is missing"),
192 )
193 })?;
194 value_mapper::decode_handler(value)
195 }
196
197 pub fn record_event<T: Value>(&mut self, name: &str, value: T) -> HandlerResult<()> {
205 require_name(name, "event name")?;
206 if !self.event_names.insert(name.to_string()) {
207 return Err(HandlerError::new(
208 "dex_sdk::HandlerError",
209 format!("event was already recorded: {name}"),
210 ));
211 }
212 self.events.push(Kv {
213 key: name.to_string(),
214 value: Some(value_mapper::encode_handler(&value)?),
215 });
216 Ok(())
217 }
218
219 pub fn get_attribute<T: Value>(&self, attribute: &Attribute<T>) -> HandlerResult<Option<T>> {
221 self.get_attribute_value(attribute.name(), PersistenceKind::Attribute, None)
222 }
223
224 pub fn get_attribute_map<T: Value>(
226 &self,
227 attribute: &AttributeMap<T>,
228 instance: &str,
229 ) -> HandlerResult<Option<T>> {
230 self.get_attribute_value(
231 attribute.name(),
232 PersistenceKind::AttributeMap,
233 Some(instance),
234 )
235 }
236
237 pub fn set_attribute<T: Value>(
239 &mut self,
240 attribute: &Attribute<T>,
241 value: T,
242 ) -> HandlerResult<()> {
243 self.set_attribute_value(
244 attribute.name(),
245 PersistenceKind::Attribute,
246 None,
247 value_mapper::encode_handler(&value)?,
248 attribute.index().map(|index| index.proto_config(false)),
249 attribute.sync_config(),
250 )
251 }
252
253 pub fn set_attribute_map<T: Value>(
255 &mut self,
256 attribute: &AttributeMap<T>,
257 instance: &str,
258 value: T,
259 ) -> HandlerResult<()> {
260 self.set_attribute_value(
261 attribute.name(),
262 PersistenceKind::AttributeMap,
263 Some(instance),
264 value_mapper::encode_handler(&value)?,
265 attribute.index().map(|index| index.proto_config(true)),
266 attribute.sync_config(),
267 )
268 }
269
270 pub fn delete_attribute<T>(&mut self, attribute: &Attribute<T>) -> HandlerResult<()> {
272 self.set_attribute_value(
273 attribute.name(),
274 PersistenceKind::Attribute,
275 None,
276 value_mapper::deletion(),
277 attribute.index().map(|index| index.proto_config(false)),
278 attribute.sync_config(),
279 )
280 }
281
282 pub fn delete_attribute_map<T>(
284 &mut self,
285 attribute: &AttributeMap<T>,
286 instance: &str,
287 ) -> HandlerResult<()> {
288 self.set_attribute_value(
289 attribute.name(),
290 PersistenceKind::AttributeMap,
291 Some(instance),
292 value_mapper::deletion(),
293 attribute.index().map(|index| index.proto_config(true)),
294 attribute.sync_config(),
295 )
296 }
297
298 pub(crate) fn attribute_map_keys<T>(
299 &self,
300 attribute: &AttributeMap<T>,
301 ) -> HandlerResult<Vec<String>> {
302 self.registered_name(
303 attribute.name(),
304 PersistenceKind::AttributeMap,
305 Some("key-check"),
306 )?;
307 let prefix = format!("{}/", attribute.name());
308 let mut physical_keys = self
309 .attributes
310 .keys()
311 .filter(|key| key.starts_with(&prefix))
312 .cloned()
313 .collect::<HashSet<_>>();
314 for (key, write) in &self.attribute_writes {
315 if !key.starts_with(&prefix) {
316 continue;
317 }
318 if matches!(
319 write.value.as_ref().and_then(|value| value.kind.as_ref()),
320 Some(value::Kind::NullValue(_))
321 ) {
322 physical_keys.remove(key);
323 } else {
324 physical_keys.insert(key.clone());
325 }
326 }
327 sorted_instance_keys(&prefix, physical_keys)
328 }
329
330 pub fn publish<T: Value>(&mut self, channel: &Channel<T>, value: T) -> HandlerResult<()> {
332 self.publish_value(
333 channel.name(),
334 PersistenceKind::Channel,
335 None,
336 value_mapper::encode_handler(&value)?,
337 )
338 }
339
340 pub fn publish_map<T: Value>(
342 &mut self,
343 channel: &ChannelMap<T>,
344 instance: &str,
345 value: T,
346 ) -> HandlerResult<()> {
347 self.publish_value(
348 channel.name(),
349 PersistenceKind::ChannelMap,
350 Some(instance),
351 value_mapper::encode_handler(&value)?,
352 )
353 }
354
355 pub fn channel_size<T>(&self, channel: &Channel<T>) -> HandlerResult<usize> {
357 self.channel_size_value(channel.name(), PersistenceKind::Channel, None)
358 }
359
360 pub fn channel_map_size<T>(
362 &self,
363 channel: &ChannelMap<T>,
364 instance: &str,
365 ) -> HandlerResult<usize> {
366 self.channel_size_value(channel.name(), PersistenceKind::ChannelMap, Some(instance))
367 }
368
369 pub(crate) fn channel_map_keys<T>(
370 &self,
371 channel: &ChannelMap<T>,
372 ) -> HandlerResult<Vec<String>> {
373 if self.method != InvocationMethod::Rpc {
374 return Err(HandlerError::new(
375 "dex_sdk::HandlerError",
376 "ChannelMap introspection requires an RPC invocation",
377 ));
378 }
379 self.registered_name(
380 channel.name(),
381 PersistenceKind::ChannelMap,
382 Some("key-check"),
383 )?;
384 let prefix = format!("{}/", channel.name());
385 let physical_keys = self
386 .channel_infos
387 .iter()
388 .filter(|(key, info)| key.starts_with(&prefix) && info.size > 0)
389 .map(|(key, _)| key.clone())
390 .collect::<HashSet<_>>();
391 sorted_instance_keys(&prefix, physical_keys)
392 }
393
394 pub fn channel_results<T: Value>(&self, channel: &Channel<T>) -> HandlerResult<Vec<T>> {
396 self.channel_results_value(channel.name(), PersistenceKind::Channel, None)
397 }
398
399 pub fn channel_map_results<T: Value>(
401 &self,
402 channel: &ChannelMap<T>,
403 instance: &str,
404 ) -> HandlerResult<Vec<T>> {
405 self.channel_results_value(channel.name(), PersistenceKind::ChannelMap, Some(instance))
406 }
407
408 pub(crate) fn new(
409 method: InvocationMethod,
410 flow: RegisteredFlow,
411 metadata: ProtoContext,
412 attributes: Vec<Kv>,
413 locals: Vec<Kv>,
414 condition_results: Option<ConditionResults>,
415 channel_infos: HashMap<String, ChannelInfo>,
416 ) -> HandlerResult<Self> {
417 Ok(Self {
418 method,
419 flow,
420 metadata,
421 attributes: map_values("Attribute", attributes)?,
422 locals: map_values("step-execution local", locals)?,
423 condition_results,
424 channel_infos,
425 attribute_writes: HashMap::new(),
426 local_writes: HashMap::new(),
427 events: Vec::new(),
428 event_names: HashSet::new(),
429 publications: Vec::new(),
430 cancellation: InvocationCancellation::new(),
431 })
432 }
433
434 pub(crate) fn cancellation(&self) -> InvocationCancellation {
435 self.cancellation.clone()
436 }
437
438 pub(crate) fn take_outputs(
439 self,
440 ) -> (Vec<AttributeWrite>, Vec<Kv>, Vec<Kv>, Vec<ChannelMessage>) {
441 (
442 self.attribute_writes.into_values().collect(),
443 self.local_writes.into_values().collect(),
444 self.events,
445 self.publications,
446 )
447 }
448
449 fn get_attribute_value<T: Value>(
450 &self,
451 name: &str,
452 kind: PersistenceKind,
453 instance: Option<&str>,
454 ) -> HandlerResult<Option<T>> {
455 let key = self.registered_name(name, kind, instance)?;
456 if let Some(write) = self.attribute_writes.get(&key) {
457 let value = write.value.as_ref().ok_or_else(|| {
458 HandlerError::new(
459 "dex_sdk::HandlerError",
460 format!("Attribute write {key} has no Value"),
461 )
462 })?;
463 if matches!(value.kind, Some(value::Kind::NullValue(_))) {
464 return Ok(None);
465 }
466 return value_mapper::decode_handler(value).map(Some);
467 }
468 self.attributes
469 .get(&key)
470 .map(value_mapper::decode_handler)
471 .transpose()
472 }
473
474 fn set_attribute_value(
475 &mut self,
476 name: &str,
477 kind: PersistenceKind,
478 instance: Option<&str>,
479 value: ProtoValue,
480 index_config: Option<dex_protocol::dex::IndexConfig>,
481 sync_config: Option<dex_protocol::dex::AttributeSyncConfig>,
482 ) -> HandlerResult<()> {
483 let key = self.registered_name(name, kind, instance)?;
484 self.attribute_writes.insert(
485 key.clone(),
486 AttributeWrite {
487 key,
488 value: Some(value),
489 index_config,
490 sync_config,
491 },
492 );
493 Ok(())
494 }
495
496 fn publish_value(
497 &mut self,
498 name: &str,
499 kind: PersistenceKind,
500 instance: Option<&str>,
501 value: ProtoValue,
502 ) -> HandlerResult<()> {
503 let channel_name = self.registered_name(name, kind, instance)?;
504 self.publications.push(ChannelMessage {
505 channel_name: channel_name.clone(),
506 value: Some(value),
507 });
508 if self.method == InvocationMethod::Rpc {
509 self.channel_infos
510 .entry(channel_name)
511 .and_modify(|info| info.size += 1)
512 .or_insert(ChannelInfo { size: 1 });
513 }
514 Ok(())
515 }
516
517 fn channel_size_value(
518 &self,
519 name: &str,
520 kind: PersistenceKind,
521 instance: Option<&str>,
522 ) -> HandlerResult<usize> {
523 let channel_name = self.registered_name(name, kind, instance)?;
524 Ok(self
525 .channel_infos
526 .get(&channel_name)
527 .map_or(0, |info| usize::try_from(info.size).unwrap_or_default()))
528 }
529
530 fn channel_results_value<T: Value>(
531 &self,
532 name: &str,
533 kind: PersistenceKind,
534 instance: Option<&str>,
535 ) -> HandlerResult<Vec<T>> {
536 let channel_name = self.registered_name(name, kind, instance)?;
537 let Some(results) = &self.condition_results else {
538 return Ok(Vec::new());
539 };
540 let mut decoded = Vec::new();
541 for result in &results.channel_results {
542 if result.channel_name == channel_name
543 && result.condition_status == ConditionStatus::Completed as i32
544 {
545 for value in &result.values {
546 decoded.push(value_mapper::decode_handler(value)?);
547 }
548 }
549 }
550 Ok(decoded)
551 }
552
553 fn registered_name(
554 &self,
555 name: &str,
556 kind: PersistenceKind,
557 instance: Option<&str>,
558 ) -> HandlerResult<String> {
559 let definition = self.flow.persistence.get(name).ok_or_else(|| {
560 HandlerError::new(
561 "dex_sdk::HandlerError",
562 format!("persistence definition does not belong to Flow: {name}"),
563 )
564 })?;
565 if definition.kind != kind {
566 return Err(HandlerError::new(
567 "dex_sdk::HandlerError",
568 format!("persistence definition kind does not match: {name}"),
569 ));
570 }
571 match kind {
572 PersistenceKind::AttributeMap | PersistenceKind::ChannelMap => instance
573 .map(|instance| physical_name(name, instance))
574 .ok_or_else(|| {
575 HandlerError::new(
576 "dex_sdk::HandlerError",
577 format!("{name} requires an instance"),
578 )
579 }),
580 PersistenceKind::Attribute | PersistenceKind::Channel if instance.is_none() => {
581 Ok(name.to_string())
582 }
583 _ => Err(HandlerError::new(
584 "dex_sdk::HandlerError",
585 format!("static persistence definition cannot use an instance: {name}"),
586 )),
587 }
588 }
589}
590
591fn sorted_instance_keys(
592 prefix: &str,
593 physical_keys: HashSet<String>,
594) -> HandlerResult<Vec<String>> {
595 let mut keys = physical_keys
596 .iter()
597 .map(|key| {
598 decode_instance(key, prefix)
599 .map_err(|error| HandlerError::new("dex_sdk::HandlerError", error))
600 })
601 .collect::<HandlerResult<Vec<_>>>()?;
602 keys.sort();
603 Ok(keys)
604}
605
606#[derive(Clone)]
607pub(crate) struct InvocationCancellation {
608 inner: Arc<InvocationCancellationInner>,
609}
610
611struct InvocationCancellationInner {
612 cancelled: AtomicBool,
613 mutex: Mutex<()>,
614 condition: Condvar,
615}
616
617impl InvocationCancellation {
618 fn new() -> Self {
619 Self {
620 inner: Arc::new(InvocationCancellationInner {
621 cancelled: AtomicBool::new(false),
622 mutex: Mutex::new(()),
623 condition: Condvar::new(),
624 }),
625 }
626 }
627
628 fn wait(&self) {
629 let mut guard = self.inner.mutex.lock().expect("cancellation lock");
630 while !self.is_cancelled() {
631 guard = self.inner.condition.wait(guard).expect("cancellation lock");
632 }
633 }
634
635 fn is_cancelled(&self) -> bool {
636 self.inner.cancelled.load(Ordering::Acquire)
637 }
638
639 pub(crate) fn cancel(&self) {
640 let _guard = self.inner.mutex.lock().expect("cancellation lock");
641 self.inner.cancelled.store(true, Ordering::Release);
642 self.inner.condition.notify_all();
643 }
644}
645
646fn system_time(timestamp: i64) -> SystemTime {
647 if timestamp >= 0 {
648 SystemTime::UNIX_EPOCH + Duration::from_secs(timestamp as u64)
649 } else {
650 SystemTime::UNIX_EPOCH - Duration::from_secs(timestamp.unsigned_abs())
651 }
652}
653
654fn map_values(kind: &str, entries: Vec<Kv>) -> HandlerResult<HashMap<String, ProtoValue>> {
655 let mut mapped = HashMap::new();
656 for entry in entries {
657 require_name(&entry.key, kind)?;
658 let value = entry.value.ok_or_else(|| {
659 HandlerError::new(
660 "dex_sdk::HandlerError",
661 format!("{kind} {} has no Value", entry.key),
662 )
663 })?;
664 if mapped.insert(entry.key.clone(), value).is_some() {
665 return Err(HandlerError::new(
666 "dex_sdk::HandlerError",
667 format!("duplicate {kind} {}", entry.key),
668 ));
669 }
670 }
671 Ok(mapped)
672}
673
674fn require_name(value: &str, kind: &str) -> HandlerResult<()> {
675 if value.is_empty() {
676 Err(HandlerError::new(
677 "dex_sdk::HandlerError",
678 format!("{kind} is required"),
679 ))
680 } else {
681 Ok(())
682 }
683}
684
685#[cfg(test)]
686mod tests {
687 use super::{Context, InvocationMethod};
688 use crate::{
689 AttributeMap, ChannelMap, Flow, PersistenceSchema, Registry, registry::physical_name,
690 value_mapper,
691 };
692 use dex_protocol::dex::{ChannelInfo, Context as ProtoContext, Kv};
693 use std::collections::HashMap;
694
695 struct MapFlow {
696 attributes: AttributeMap<String>,
697 channels: ChannelMap<String>,
698 }
699
700 impl Flow for MapFlow {
701 type StartInput = ();
702
703 fn persistence(&self) -> PersistenceSchema {
704 PersistenceSchema::new()
705 .attribute_map(&self.attributes)
706 .channel_map(&self.channels)
707 }
708 }
709
710 #[test]
711 fn map_introspection_tracks_buffered_changes() {
712 let flow = MapFlow {
713 attributes: AttributeMap::new("items"),
714 channels: ChannelMap::new("messages"),
715 };
716 let registry = Registry::new().register(flow).expect("register map Flow");
717 let registered = registry.flow("MapFlow").expect("lookup map Flow").clone();
718 let attributes = AttributeMap::<String>::new("items");
719 let channels = ChannelMap::<String>::new("messages");
720 let special = "special / key";
721 let mut context = Context::new(
722 InvocationMethod::Rpc,
723 registered,
724 ProtoContext::default(),
725 vec![
726 Kv {
727 key: physical_name("items", special),
728 value: Some(value_mapper::encode_handler(&"initial".to_string()).unwrap()),
729 },
730 Kv {
731 key: physical_name("items", "z"),
732 value: Some(value_mapper::encode_handler(&"remove".to_string()).unwrap()),
733 },
734 ],
735 Vec::new(),
736 None,
737 HashMap::from([
738 (physical_name("messages", special), ChannelInfo { size: 1 }),
739 (physical_name("messages", "empty"), ChannelInfo { size: 0 }),
740 ]),
741 )
742 .expect("create RPC Context");
743
744 assert_eq!(
745 vec![special.to_string(), "z".to_string()],
746 attributes.all_instance_keys(&context).unwrap()
747 );
748 attributes
749 .set(&mut context, "a", "added".to_string())
750 .unwrap();
751 attributes.delete(&mut context, "z").unwrap();
752 assert_eq!(
753 vec!["a".to_string(), special.to_string()],
754 attributes.all_instance_keys(&context).unwrap()
755 );
756 assert_eq!(2, attributes.map_size(&context).unwrap());
757
758 assert_eq!(
759 vec![special.to_string()],
760 channels.all_instance_keys(&context).unwrap()
761 );
762 channels
763 .publish(&mut context, "a", "published".to_string())
764 .unwrap();
765 assert_eq!(
766 vec!["a".to_string(), special.to_string()],
767 channels.all_instance_keys(&context).unwrap()
768 );
769 assert_eq!(2, channels.map_size(&context).unwrap());
770 }
771}