1#![allow(dead_code)]
2use std::cmp::Ordering;
8use std::collections::BinaryHeap;
9use std::time::{Duration, Instant};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
13pub enum MessagePriority {
14 Low = 0,
16 Normal = 1,
18 High = 2,
20 Critical = 3,
22}
23
24impl MessagePriority {
25 #[must_use]
27 pub fn numeric_value(self) -> u8 {
28 self as u8
29 }
30
31 #[must_use]
33 pub fn escalate(self) -> Self {
34 match self {
35 MessagePriority::Low => MessagePriority::Normal,
36 MessagePriority::Normal => MessagePriority::High,
37 MessagePriority::High | MessagePriority::Critical => MessagePriority::Critical,
38 }
39 }
40}
41
42#[derive(Debug, Clone)]
44pub struct DistributedMessage {
45 pub id: u64,
47 pub priority: MessagePriority,
49 pub payload: Vec<u8>,
51 pub enqueued_at: Instant,
53 pub ttl: Option<Duration>,
55 pub source: String,
57 pub destination: String,
59}
60
61impl DistributedMessage {
62 #[must_use]
64 pub fn new(
65 id: u64,
66 priority: MessagePriority,
67 payload: Vec<u8>,
68 source: impl Into<String>,
69 destination: impl Into<String>,
70 ) -> Self {
71 Self {
72 id,
73 priority,
74 payload,
75 enqueued_at: Instant::now(),
76 ttl: None,
77 source: source.into(),
78 destination: destination.into(),
79 }
80 }
81
82 #[must_use]
84 pub fn with_ttl(mut self, ttl: Duration) -> Self {
85 self.ttl = Some(ttl);
86 self
87 }
88
89 #[must_use]
93 pub fn is_expired(&self, now: Instant) -> bool {
94 match self.ttl {
95 None => false,
96 Some(ttl) => now.saturating_duration_since(self.enqueued_at) >= ttl,
97 }
98 }
99
100 #[must_use]
102 pub fn payload_len(&self) -> usize {
103 self.payload.len()
104 }
105}
106
107#[derive(Debug)]
110struct QueueEntry {
111 message: DistributedMessage,
112 seq: u64,
113}
114
115impl PartialEq for QueueEntry {
116 fn eq(&self, other: &Self) -> bool {
117 self.message.priority == other.message.priority && self.seq == other.seq
118 }
119}
120
121impl Eq for QueueEntry {}
122
123impl PartialOrd for QueueEntry {
124 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
125 Some(self.cmp(other))
126 }
127}
128
129impl Ord for QueueEntry {
130 fn cmp(&self, other: &Self) -> Ordering {
131 match self.message.priority.cmp(&other.message.priority) {
133 Ordering::Equal => other.seq.cmp(&self.seq),
134 ord => ord,
135 }
136 }
137}
138
139#[derive(Debug)]
141pub struct MessageQueue {
142 heap: BinaryHeap<QueueEntry>,
143 capacity: usize,
144 seq_counter: u64,
145 enqueued_total: u64,
146 dropped_total: u64,
147}
148
149impl MessageQueue {
150 #[must_use]
152 pub fn new(capacity: usize) -> Self {
153 Self {
154 heap: BinaryHeap::new(),
155 capacity,
156 seq_counter: 0,
157 enqueued_total: 0,
158 dropped_total: 0,
159 }
160 }
161
162 #[must_use]
164 pub fn len(&self) -> usize {
165 self.heap.len()
166 }
167
168 #[must_use]
170 pub fn is_empty(&self) -> bool {
171 self.heap.is_empty()
172 }
173
174 pub fn enqueue(&mut self, message: DistributedMessage) -> bool {
179 if self.heap.len() >= self.capacity {
180 self.dropped_total += 1;
181 return false;
182 }
183 let seq = self.seq_counter;
184 self.seq_counter += 1;
185 self.enqueued_total += 1;
186 self.heap.push(QueueEntry { message, seq });
187 true
188 }
189
190 pub fn dequeue(&mut self) -> Option<DistributedMessage> {
192 self.heap.pop().map(|e| e.message)
193 }
194
195 #[must_use]
197 pub fn peek_priority(&self) -> Option<MessagePriority> {
198 self.heap.peek().map(|e| e.message.priority)
199 }
200
201 #[must_use]
203 pub fn enqueued_total(&self) -> u64 {
204 self.enqueued_total
205 }
206
207 #[must_use]
209 pub fn dropped_total(&self) -> u64 {
210 self.dropped_total
211 }
212
213 pub fn drain_expired(&mut self, now: Instant) -> usize {
216 let before = self.heap.len();
217 let entries: Vec<QueueEntry> = self.heap.drain().collect();
218 for entry in entries {
219 if !entry.message.is_expired(now) {
220 self.heap.push(entry);
221 }
222 }
223 before - self.heap.len()
224 }
225}
226
227#[cfg(test)]
228mod tests {
229 use super::*;
230 use std::time::Duration;
231
232 fn msg(id: u64, priority: MessagePriority) -> DistributedMessage {
233 DistributedMessage::new(id, priority, vec![1, 2, 3], "src", "dst")
234 }
235
236 #[test]
237 fn test_priority_numeric_values() {
238 assert_eq!(MessagePriority::Low.numeric_value(), 0);
239 assert_eq!(MessagePriority::Normal.numeric_value(), 1);
240 assert_eq!(MessagePriority::High.numeric_value(), 2);
241 assert_eq!(MessagePriority::Critical.numeric_value(), 3);
242 }
243
244 #[test]
245 fn test_priority_ordering() {
246 assert!(MessagePriority::Critical > MessagePriority::High);
247 assert!(MessagePriority::High > MessagePriority::Normal);
248 assert!(MessagePriority::Normal > MessagePriority::Low);
249 }
250
251 #[test]
252 fn test_priority_escalate() {
253 assert_eq!(MessagePriority::Low.escalate(), MessagePriority::Normal);
254 assert_eq!(MessagePriority::Normal.escalate(), MessagePriority::High);
255 assert_eq!(MessagePriority::High.escalate(), MessagePriority::Critical);
256 assert_eq!(
257 MessagePriority::Critical.escalate(),
258 MessagePriority::Critical
259 );
260 }
261
262 #[test]
263 fn test_message_not_expired_without_ttl() {
264 let m = msg(1, MessagePriority::Normal);
265 assert!(!m.is_expired(Instant::now()));
266 }
267
268 #[test]
269 fn test_message_not_expired_within_ttl() {
270 let m = msg(1, MessagePriority::Normal).with_ttl(Duration::from_secs(60));
271 assert!(!m.is_expired(Instant::now()));
272 }
273
274 #[test]
275 fn test_message_is_expired_after_ttl() {
276 let past = Instant::now() - Duration::from_secs(10);
277 let mut m = msg(1, MessagePriority::Normal);
278 m.enqueued_at = past;
279 let m = m.with_ttl(Duration::from_secs(5));
280 assert!(m.is_expired(Instant::now()));
281 }
282
283 #[test]
284 fn test_message_payload_len() {
285 let m = DistributedMessage::new(1, MessagePriority::Low, vec![0u8; 100], "a", "b");
286 assert_eq!(m.payload_len(), 100);
287 }
288
289 #[test]
290 fn test_queue_enqueue_and_len() {
291 let mut q = MessageQueue::new(10);
292 assert!(q.is_empty());
293 q.enqueue(msg(1, MessagePriority::Low));
294 q.enqueue(msg(2, MessagePriority::High));
295 assert_eq!(q.len(), 2);
296 }
297
298 #[test]
299 fn test_queue_dequeue_priority_order() {
300 let mut q = MessageQueue::new(10);
301 q.enqueue(msg(1, MessagePriority::Low));
302 q.enqueue(msg(2, MessagePriority::Critical));
303 q.enqueue(msg(3, MessagePriority::Normal));
304
305 assert_eq!(
306 q.dequeue().expect("dequeue should return a task").priority,
307 MessagePriority::Critical
308 );
309 assert_eq!(
310 q.dequeue().expect("dequeue should return a task").priority,
311 MessagePriority::Normal
312 );
313 assert_eq!(
314 q.dequeue().expect("dequeue should return a task").priority,
315 MessagePriority::Low
316 );
317 assert!(q.dequeue().is_none());
318 }
319
320 #[test]
321 fn test_queue_peek_priority() {
322 let mut q = MessageQueue::new(5);
323 assert!(q.peek_priority().is_none());
324 q.enqueue(msg(1, MessagePriority::Low));
325 q.enqueue(msg(2, MessagePriority::High));
326 assert_eq!(q.peek_priority(), Some(MessagePriority::High));
327 }
328
329 #[test]
330 fn test_queue_capacity_overflow() {
331 let mut q = MessageQueue::new(2);
332 assert!(q.enqueue(msg(1, MessagePriority::Low)));
333 assert!(q.enqueue(msg(2, MessagePriority::Low)));
334 assert!(!q.enqueue(msg(3, MessagePriority::Low)));
335 assert_eq!(q.dropped_total(), 1);
336 }
337
338 #[test]
339 fn test_queue_enqueued_total() {
340 let mut q = MessageQueue::new(10);
341 q.enqueue(msg(1, MessagePriority::Normal));
342 q.enqueue(msg(2, MessagePriority::Normal));
343 assert_eq!(q.enqueued_total(), 2);
344 }
345
346 #[test]
347 fn test_drain_expired() {
348 let mut q = MessageQueue::new(10);
349 let past = Instant::now() - Duration::from_secs(10);
350
351 let mut expired_msg = msg(1, MessagePriority::Low);
352 expired_msg.enqueued_at = past;
353 let expired_msg = expired_msg.with_ttl(Duration::from_secs(5));
354 q.enqueue(expired_msg);
355
356 let fresh = msg(2, MessagePriority::High).with_ttl(Duration::from_secs(60));
357 q.enqueue(fresh);
358
359 let removed = q.drain_expired(Instant::now());
360 assert_eq!(removed, 1);
361 assert_eq!(q.len(), 1);
362 }
363}