1use {
4 crate::{
5 scheduler::{OccurrenceRef, OccurrenceSource, WithdrawalReason},
6 sui,
7 },
8 serde::{Deserialize, Serialize},
9};
10
11#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
13pub struct TransactionReference {
14 digest: sui::types::Digest,
15 checkpoint: u64,
16}
17
18impl TransactionReference {
19 pub(crate) const fn new(digest: sui::types::Digest, checkpoint: u64) -> Self {
20 Self { digest, checkpoint }
21 }
22
23 pub const fn digest(&self) -> &sui::types::Digest {
25 &self.digest
26 }
27
28 pub const fn checkpoint(&self) -> u64 {
30 self.checkpoint
31 }
32}
33
34#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
36pub struct ScheduledOccurrence {
37 reference: OccurrenceRef,
38 start_time_ms: u64,
39 deadline_ms: Option<u64>,
40 priority_fee_percentage: u64,
41 source: OccurrenceSource,
42}
43
44impl ScheduledOccurrence {
45 pub(crate) const fn new(
46 reference: OccurrenceRef,
47 start_time_ms: u64,
48 deadline_ms: Option<u64>,
49 priority_fee_percentage: u64,
50 source: OccurrenceSource,
51 ) -> Self {
52 Self {
53 reference,
54 start_time_ms,
55 deadline_ms,
56 priority_fee_percentage,
57 source,
58 }
59 }
60
61 pub const fn reference(&self) -> OccurrenceRef {
63 self.reference
64 }
65
66 pub const fn start_time_ms(&self) -> u64 {
68 self.start_time_ms
69 }
70
71 pub const fn deadline_ms(&self) -> Option<u64> {
73 self.deadline_ms
74 }
75
76 pub const fn priority_fee_percentage(&self) -> u64 {
78 self.priority_fee_percentage
79 }
80
81 pub const fn source(&self) -> OccurrenceSource {
83 self.source
84 }
85}
86
87#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
89pub struct WithdrawnOccurrence {
90 reference: OccurrenceRef,
91 reason: WithdrawalReason,
92}
93
94impl WithdrawnOccurrence {
95 pub(crate) const fn new(reference: OccurrenceRef, reason: WithdrawalReason) -> Self {
96 Self { reference, reason }
97 }
98
99 pub const fn reference(&self) -> OccurrenceRef {
101 self.reference
102 }
103
104 pub const fn reason(&self) -> WithdrawalReason {
106 self.reason
107 }
108}
109
110#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
112pub struct ScheduleDelta {
113 scheduled: Vec<ScheduledOccurrence>,
114 withdrawn: Vec<WithdrawnOccurrence>,
115 advertised: Option<OccurrenceRef>,
116}
117
118impl ScheduleDelta {
119 pub(crate) fn new(
120 scheduled: Vec<ScheduledOccurrence>,
121 withdrawn: Vec<WithdrawnOccurrence>,
122 advertised: Option<OccurrenceRef>,
123 ) -> Self {
124 Self {
125 scheduled,
126 withdrawn,
127 advertised,
128 }
129 }
130
131 pub fn scheduled(&self) -> &[ScheduledOccurrence] {
133 &self.scheduled
134 }
135
136 pub fn withdrawn(&self) -> &[WithdrawnOccurrence] {
138 &self.withdrawn
139 }
140
141 pub const fn advertised(&self) -> Option<OccurrenceRef> {
143 self.advertised
144 }
145
146 pub fn is_empty(&self) -> bool {
148 self.scheduled.is_empty() && self.withdrawn.is_empty() && self.advertised.is_none()
149 }
150}
151
152#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
154pub struct TaskMutationReceipt {
155 transaction: TransactionReference,
156 task_id: sui::types::Address,
157 delta: ScheduleDelta,
158}
159
160impl TaskMutationReceipt {
161 pub(crate) const fn new(
162 transaction: TransactionReference,
163 task_id: sui::types::Address,
164 delta: ScheduleDelta,
165 ) -> Self {
166 Self {
167 transaction,
168 task_id,
169 delta,
170 }
171 }
172
173 pub const fn transaction(&self) -> &TransactionReference {
175 &self.transaction
176 }
177
178 pub const fn task_id(&self) -> sui::types::Address {
180 self.task_id
181 }
182
183 pub const fn delta(&self) -> &ScheduleDelta {
185 &self.delta
186 }
187}
188
189#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
191pub struct AbortReceipt {
192 transaction: TransactionReference,
193 occurrence: OccurrenceRef,
194 execution_id: sui::types::Address,
195}
196
197impl AbortReceipt {
198 pub(crate) const fn new(
199 transaction: TransactionReference,
200 occurrence: OccurrenceRef,
201 execution_id: sui::types::Address,
202 ) -> Self {
203 Self {
204 transaction,
205 occurrence,
206 execution_id,
207 }
208 }
209
210 pub const fn transaction(&self) -> &TransactionReference {
212 &self.transaction
213 }
214
215 pub const fn occurrence(&self) -> OccurrenceRef {
217 self.occurrence
218 }
219
220 pub const fn execution_id(&self) -> sui::types::Address {
222 self.execution_id
223 }
224}
225
226#[cfg(feature = "nexus")]
230#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
231pub struct RecoveryReceipt {
232 pub(crate) occurrence: crate::scheduler::OccurrenceSnapshot,
233 pub(crate) resolutions: Vec<crate::nexus::workflow::ExpiredWalkResolutionResult>,
234 pub(crate) settlement: Option<TaskMutationReceipt>,
235}
236
237#[cfg(feature = "nexus")]
238impl RecoveryReceipt {
239 pub const fn occurrence(&self) -> &crate::scheduler::OccurrenceSnapshot {
241 &self.occurrence
242 }
243
244 pub fn resolutions(&self) -> &[crate::nexus::workflow::ExpiredWalkResolutionResult] {
246 &self.resolutions
247 }
248
249 pub const fn settlement(&self) -> Option<&TaskMutationReceipt> {
251 self.settlement.as_ref()
252 }
253}
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258
259 fn address(value: &'static str) -> sui::types::Address {
260 sui::types::Address::from_static(value)
261 }
262
263 #[test]
264 fn mutation_receipts_preserve_transaction_and_schedule_changes() {
265 let task_id = address("0x20");
266 let reference = OccurrenceRef::new(task_id, 4);
267 let transaction = TransactionReference::new(sui::types::Digest::new([7; 32]), 12);
268 assert_eq!(transaction.digest(), &sui::types::Digest::new([7; 32]));
269 assert_eq!(transaction.checkpoint(), 12);
270
271 let scheduled = ScheduledOccurrence::new(
272 reference,
273 100,
274 Some(120),
275 30,
276 OccurrenceSource::Recurring { iteration: 2 },
277 );
278 assert_eq!(scheduled.reference(), reference);
279 assert_eq!(scheduled.start_time_ms(), 100);
280 assert_eq!(scheduled.deadline_ms(), Some(120));
281 assert_eq!(scheduled.priority_fee_percentage(), 30);
282 assert_eq!(
283 scheduled.source(),
284 OccurrenceSource::Recurring { iteration: 2 }
285 );
286
287 let withdrawn = WithdrawnOccurrence::new(reference, WithdrawalReason::RecurrenceReplaced);
288 assert_eq!(withdrawn.reference(), reference);
289 assert_eq!(withdrawn.reason(), WithdrawalReason::RecurrenceReplaced);
290
291 let delta = ScheduleDelta::new(
292 vec![scheduled.clone()],
293 vec![withdrawn.clone()],
294 Some(reference),
295 );
296 assert_eq!(delta.scheduled(), &[scheduled]);
297 assert_eq!(delta.withdrawn(), &[withdrawn]);
298 assert_eq!(delta.advertised(), Some(reference));
299 assert!(!delta.is_empty());
300 assert!(ScheduleDelta::default().is_empty());
301
302 let receipt = TaskMutationReceipt::new(transaction.clone(), task_id, delta.clone());
303 assert_eq!(receipt.transaction(), &transaction);
304 assert_eq!(receipt.task_id(), task_id);
305 assert_eq!(receipt.delta(), &delta);
306 }
307
308 #[test]
309 fn abort_receipt_identifies_the_occurrence_and_runtime_object() {
310 let transaction = TransactionReference::new(sui::types::Digest::new([8; 32]), 13);
311 let occurrence = OccurrenceRef::new(address("0x21"), 5);
312 let execution_id = address("0x22");
313 let receipt = AbortReceipt::new(transaction.clone(), occurrence, execution_id);
314
315 assert_eq!(receipt.transaction(), &transaction);
316 assert_eq!(receipt.occurrence(), occurrence);
317 assert_eq!(receipt.execution_id(), execution_id);
318 }
319}