1use crate::storage::warp_table::{
2 Capacity, WarpId, WarpTable, WarpTableCreationError, WarpTableInsertOutcome,
3 WarpTableRemoveOutcome, WarpTableRow,
4};
5use crate::{TimerArmDecision, TimerArmOutcome, TimerArmRefusal, TimerIn, TimerToken, WakeAfter};
6
7use super::Delayed;
8
9#[derive(WarpTableRow)]
10struct PendingDelayedMessage<Message> {
11 message: Message,
12}
13
14type PendingDelayedMessageCreationError<Message> = WarpTableCreationError<
15 <PendingDelayedMessage<Message> as WarpTableRow>::ColumnsCreationError,
16 <PendingDelayedMessage<Message> as WarpTableRow>::IndexesCreationError,
17>;
18
19#[derive(Clone, Copy, Debug, Eq, PartialEq)]
20pub enum DelayLedgerCreationFailure {
21 StorageUnavailable,
22}
23
24#[derive(Debug)]
25pub struct DelayLedgerCreationError<Message> {
26 failure: DelayLedgerCreationFailure,
27 _source: PendingDelayedMessageCreationError<Message>,
28}
29
30impl<Message> DelayLedgerCreationError<Message> {
31 pub const fn failure(&self) -> DelayLedgerCreationFailure {
32 self.failure
33 }
34}
35
36#[derive(Clone, Copy, Debug, Eq, PartialEq)]
37pub enum DelayUnavailable {
38 AtCapacity { maximum_pending: u32 },
39 IdentifiersExhausted,
40 WakeRefused { reason: TimerArmRefusal },
41}
42
43pub struct DelayLedger<Message> {
44 pending: WarpTable<PendingDelayedMessage<Message>>,
45}
46
47impl<Message> DelayLedger<Message> {
48 pub fn try_new(capacity: Capacity) -> Result<Self, DelayLedgerCreationError<Message>> {
49 let pending = WarpTable::try_new(capacity).map_err(|source| DelayLedgerCreationError {
50 failure: DelayLedgerCreationFailure::StorageUnavailable,
51 _source: source,
52 })?;
53 Ok(Self { pending })
54 }
55
56 pub const fn capacity(&self) -> Capacity {
57 self.pending.capacity()
58 }
59
60 pub const fn len(&self) -> usize {
61 self.pending.len()
62 }
63
64 pub const fn is_empty(&self) -> bool {
65 self.pending.is_empty()
66 }
67
68 pub fn admit(&mut self, delayed: Delayed<Message>) -> DelayAdmissionOutcome<Message> {
69 let (message, wake_after) = delayed.into_parts();
70 match self.pending.insert(PendingDelayedMessage { message }) {
71 WarpTableInsertOutcome::Inserted { id, .. } => DelayAdmissionOutcome::Admitted {
72 admission: DelayAdmission {
73 token: TimerToken::from_nonzero(id.value()),
74 wake_after,
75 },
76 },
77 WarpTableInsertOutcome::Full {
78 rejected: PendingDelayedMessage { message },
79 } => DelayAdmissionOutcome::Refused {
80 reason: DelayUnavailable::AtCapacity {
81 maximum_pending: self.capacity().value().get(),
82 },
83 rejected: Delayed::new(message, wake_after),
84 },
85 WarpTableInsertOutcome::IdentifiersExhausted {
86 rejected: PendingDelayedMessage { message },
87 } => DelayAdmissionOutcome::Refused {
88 reason: DelayUnavailable::IdentifiersExhausted,
89 rejected: Delayed::new(message, wake_after),
90 },
91 WarpTableInsertOutcome::UniqueConflict {
92 conflict,
93 rejected: _,
94 } => match conflict {},
95 }
96 }
97
98 pub fn refuse(&mut self, refusal: DelayArmRefusal) -> DelayRefusalOutcome<Message> {
99 match self.pending.remove(WarpId::new(refusal.token.value())) {
100 WarpTableRemoveOutcome::Removed {
101 removed: PendingDelayedMessage { message },
102 ..
103 } => DelayRefusalOutcome::Refused {
104 reason: DelayUnavailable::WakeRefused {
105 reason: refusal.reason,
106 },
107 rejected: Delayed::new(message, refusal.wake_after),
108 },
109 WarpTableRemoveOutcome::Absent { .. } => DelayRefusalOutcome::Stale { refusal },
110 }
111 }
112
113 pub fn withdraw(&mut self, admission: DelayAdmission) -> DelayWithdrawalOutcome<Message> {
114 match self.pending.remove(WarpId::new(admission.token.value())) {
115 WarpTableRemoveOutcome::Removed {
116 removed: PendingDelayedMessage { message },
117 ..
118 } => DelayWithdrawalOutcome::Withdrawn {
119 rejected: Delayed::new(message, admission.wake_after),
120 },
121 WarpTableRemoveOutcome::Absent { .. } => DelayWithdrawalOutcome::Absent { admission },
122 }
123 }
124
125 pub fn release(&mut self, token: TimerToken) -> DelayReleaseOutcome<Message> {
126 match self.pending.remove(WarpId::new(token.value())) {
127 WarpTableRemoveOutcome::Removed {
128 removed: PendingDelayedMessage { message },
129 ..
130 } => DelayReleaseOutcome::Released { message },
131 WarpTableRemoveOutcome::Absent { .. } => DelayReleaseOutcome::Stale { token },
132 }
133 }
134}
135
136#[must_use = "admitted delayed work or exact refusal must be handled by the fitting"]
137pub enum DelayAdmissionOutcome<Message> {
138 Admitted {
139 admission: DelayAdmission,
140 },
141 Refused {
142 reason: DelayUnavailable,
143 rejected: Delayed<Message>,
144 },
145}
146
147#[must_use = "delayed-message admission must be armed before returning"]
148pub struct DelayAdmission {
149 token: TimerToken,
150 wake_after: WakeAfter,
151}
152
153impl DelayAdmission {
154 pub const fn timer_input(&self) -> TimerIn<TimerToken> {
155 TimerIn::Arm {
156 token: self.token,
157 after: self.wake_after,
158 }
159 }
160
161 pub fn settle_timer_arm(
162 self,
163 outcome: TimerArmOutcome<TimerToken>,
164 ) -> Result<DelayArmOutcome, DelayArmMismatch> {
165 let decision = outcome.into_decision();
166 let matches_admission = match decision {
167 TimerArmDecision::Armed { token } => token == self.token,
168 TimerArmDecision::Refused { token, after, .. } => {
169 token == self.token && after == self.wake_after
170 }
171 };
172 if !matches_admission {
173 return Err(DelayArmMismatch {
174 admission: self,
175 decision,
176 });
177 }
178 match decision {
179 TimerArmDecision::Armed { .. } => Ok(DelayArmOutcome::Armed),
180 TimerArmDecision::Refused { reason, .. } => Ok(DelayArmOutcome::Refused {
181 refusal: DelayArmRefusal {
182 token: self.token,
183 wake_after: self.wake_after,
184 reason,
185 },
186 }),
187 }
188 }
189}
190
191#[must_use = "armed delayed work or exact timer refusal must be handled by the fitting"]
192pub enum DelayArmOutcome {
193 Armed,
194 Refused { refusal: DelayArmRefusal },
195}
196
197pub struct DelayArmMismatch {
198 admission: DelayAdmission,
199 decision: TimerArmDecision<TimerToken>,
200}
201
202impl DelayArmMismatch {
203 pub fn into_parts(self) -> (DelayAdmission, TimerArmDecision<TimerToken>) {
204 (self.admission, self.decision)
205 }
206}
207
208#[must_use = "timer refusal must be returned to the delay ledger"]
209pub struct DelayArmRefusal {
210 token: TimerToken,
211 wake_after: WakeAfter,
212 reason: TimerArmRefusal,
213}
214
215impl DelayArmRefusal {
216 pub const fn reason(&self) -> TimerArmRefusal {
217 self.reason
218 }
219}
220
221#[must_use = "resolved or stale timer refusal must be handled by the fitting"]
222pub enum DelayRefusalOutcome<Message> {
223 Refused {
224 reason: DelayUnavailable,
225 rejected: Delayed<Message>,
226 },
227 Stale {
228 refusal: DelayArmRefusal,
229 },
230}
231
232#[must_use = "released or stale timer tokens must be handled by the fitting"]
233pub enum DelayReleaseOutcome<Message> {
234 Released { message: Message },
235 Stale { token: TimerToken },
236}
237
238#[must_use = "withdrawn delayed work or an absent admission must be handled by the fitting"]
239pub enum DelayWithdrawalOutcome<Message> {
240 Withdrawn { rejected: Delayed<Message> },
241 Absent { admission: DelayAdmission },
242}