telltale_runtime/effects/handlers/
recording.rs1use async_trait::async_trait;
7use serde::{de::DeserializeOwned, Serialize};
8use std::time::Duration;
9
10use crate::effects::{ChoreoHandler, ChoreoResult, ChoreographyError, RoleId};
11
12#[derive(Clone)]
14pub struct RecordingHandler<R: RoleId> {
15 pub events: std::sync::Arc<std::sync::Mutex<Vec<RecordedEvent<R>>>>,
16 role: R,
17}
18
19#[derive(Debug, Clone)]
20pub enum RecordedEvent<R: RoleId> {
21 Send { from: R, to: R, msg_type: String },
22 Recv { from: R, to: R, msg_type: String },
23 Choose { at: R, label: <R as RoleId>::Label },
24 Offer { from: R, to: R },
25}
26
27impl<R: RoleId> RecordingHandler<R> {
28 pub fn new(role: R) -> Self {
29 Self {
30 events: std::sync::Arc::new(std::sync::Mutex::new(Vec::new())),
31 role,
32 }
33 }
34
35 pub fn events(&self) -> Vec<RecordedEvent<R>> {
36 self.events
37 .lock()
38 .unwrap_or_else(std::sync::PoisonError::into_inner)
39 .clone()
40 }
41
42 pub fn clear(&self) {
43 self.events
44 .lock()
45 .unwrap_or_else(std::sync::PoisonError::into_inner)
46 .clear();
47 }
48}
49
50#[async_trait]
51impl<R: RoleId + 'static> ChoreoHandler for RecordingHandler<R> {
52 type Role = R;
53 type Endpoint = ();
54
55 async fn send<M: Serialize + Send + Sync>(
56 &mut self,
57 _ep: &mut Self::Endpoint,
58 to: Self::Role,
59 _msg: &M,
60 ) -> ChoreoResult<()> {
61 self.events
62 .lock()
63 .unwrap_or_else(std::sync::PoisonError::into_inner)
64 .push(RecordedEvent::Send {
65 from: self.role,
66 to,
67 msg_type: std::any::type_name::<M>().to_string(),
68 });
69 Ok(())
70 }
71
72 async fn recv<M: DeserializeOwned + Send>(
73 &mut self,
74 _ep: &mut Self::Endpoint,
75 from: Self::Role,
76 ) -> ChoreoResult<M> {
77 self.events
78 .lock()
79 .unwrap_or_else(std::sync::PoisonError::into_inner)
80 .push(RecordedEvent::Recv {
81 from,
82 to: self.role,
83 msg_type: std::any::type_name::<M>().to_string(),
84 });
85 Err(ChoreographyError::Transport(
86 "RecordingHandler cannot produce values".into(),
87 ))
88 }
89
90 async fn choose(
91 &mut self,
92 _ep: &mut Self::Endpoint,
93 at: Self::Role,
94 label: <Self::Role as RoleId>::Label,
95 ) -> ChoreoResult<()> {
96 self.events
97 .lock()
98 .unwrap_or_else(std::sync::PoisonError::into_inner)
99 .push(RecordedEvent::Choose { at, label });
100 Ok(())
101 }
102
103 async fn offer(
104 &mut self,
105 _ep: &mut Self::Endpoint,
106 from: Self::Role,
107 ) -> ChoreoResult<<Self::Role as RoleId>::Label> {
108 self.events
109 .lock()
110 .unwrap_or_else(std::sync::PoisonError::into_inner)
111 .push(RecordedEvent::Offer {
112 from,
113 to: self.role,
114 });
115 Err(ChoreographyError::Transport(
116 "RecordingHandler cannot produce labels".into(),
117 ))
118 }
119
120 async fn with_timeout<F, T>(
121 &mut self,
122 _ep: &mut Self::Endpoint,
123 _at: Self::Role,
124 _dur: Duration,
125 body: F,
126 ) -> ChoreoResult<T>
127 where
128 F: std::future::Future<Output = ChoreoResult<T>> + Send,
129 {
130 body.await
131 }
132}