dynomite/msg/queue.rs
1//! Owning queue of [`Msg`] values.
2//!
3//! Messages thread through [`MsgQueue`] lists backed by a
4//! [`VecDeque`]. The owning relationship is
5//! straightforward: pushing a [`Msg`] into a queue transfers
6//! ownership; popping one transfers it back to the caller.
7//!
8//! [`Msg`]: super::message::Msg
9
10use std::collections::VecDeque;
11
12use crate::core::types::MsgId;
13
14use super::message::Msg;
15
16/// Ordered queue of owned messages.
17#[derive(Debug, Default)]
18pub struct MsgQueue {
19 inner: VecDeque<Msg>,
20}
21
22impl<'a> IntoIterator for &'a MsgQueue {
23 type Item = &'a Msg;
24 type IntoIter = std::collections::vec_deque::Iter<'a, Msg>;
25
26 fn into_iter(self) -> Self::IntoIter {
27 self.inner.iter()
28 }
29}
30
31impl MsgQueue {
32 /// Build an empty queue.
33 ///
34 /// # Examples
35 ///
36 /// ```
37 /// use dynomite::msg::MsgQueue;
38 /// let q = MsgQueue::new();
39 /// assert!(q.is_empty());
40 /// ```
41 #[must_use]
42 pub fn new() -> Self {
43 Self {
44 inner: VecDeque::new(),
45 }
46 }
47
48 /// Build an empty queue with capacity for at least `n` messages.
49 ///
50 /// # Examples
51 ///
52 /// ```
53 /// use dynomite::msg::MsgQueue;
54 /// let q = MsgQueue::with_capacity(8);
55 /// assert!(q.is_empty());
56 /// ```
57 #[must_use]
58 pub fn with_capacity(n: usize) -> Self {
59 Self {
60 inner: VecDeque::with_capacity(n),
61 }
62 }
63
64 /// Append `msg` to the tail of the queue.
65 ///
66 /// # Examples
67 ///
68 /// ```
69 /// use dynomite::msg::{Msg, MsgQueue, MsgType};
70 ///
71 /// let mut q = MsgQueue::new();
72 /// q.push_back(Msg::new(7, MsgType::ReqRedisGet, true));
73 /// assert_eq!(q.len(), 1);
74 /// ```
75 pub fn push_back(&mut self, msg: Msg) {
76 self.inner.push_back(msg);
77 }
78
79 /// Push `msg` to the head of the queue.
80 ///
81 /// # Examples
82 ///
83 /// ```
84 /// use dynomite::msg::{Msg, MsgQueue, MsgType};
85 ///
86 /// let mut q = MsgQueue::new();
87 /// q.push_front(Msg::new(1, MsgType::ReqMcGet, true));
88 /// assert_eq!(q.len(), 1);
89 /// ```
90 pub fn push_front(&mut self, msg: Msg) {
91 self.inner.push_front(msg);
92 }
93
94 /// Remove and return the head of the queue.
95 ///
96 /// # Examples
97 ///
98 /// ```
99 /// use dynomite::msg::{Msg, MsgQueue, MsgType};
100 ///
101 /// let mut q = MsgQueue::new();
102 /// q.push_back(Msg::new(1, MsgType::ReqMcGet, true));
103 /// assert!(q.pop_front().is_some());
104 /// assert!(q.pop_front().is_none());
105 /// ```
106 pub fn pop_front(&mut self) -> Option<Msg> {
107 self.inner.pop_front()
108 }
109
110 /// Remove and return the tail of the queue.
111 ///
112 /// # Examples
113 ///
114 /// ```
115 /// use dynomite::msg::{Msg, MsgQueue, MsgType};
116 ///
117 /// let mut q = MsgQueue::new();
118 /// q.push_back(Msg::new(1, MsgType::ReqMcGet, true));
119 /// q.push_back(Msg::new(2, MsgType::ReqMcGet, true));
120 /// assert_eq!(q.pop_back().unwrap().id(), 2);
121 /// ```
122 pub fn pop_back(&mut self) -> Option<Msg> {
123 self.inner.pop_back()
124 }
125
126 /// Number of messages currently in the queue.
127 ///
128 /// # Examples
129 ///
130 /// ```
131 /// use dynomite::msg::MsgQueue;
132 /// assert_eq!(MsgQueue::new().len(), 0);
133 /// ```
134 #[must_use]
135 pub fn len(&self) -> usize {
136 self.inner.len()
137 }
138
139 /// True when the queue is empty.
140 ///
141 /// # Examples
142 ///
143 /// ```
144 /// use dynomite::msg::MsgQueue;
145 /// assert!(MsgQueue::new().is_empty());
146 /// ```
147 #[must_use]
148 pub fn is_empty(&self) -> bool {
149 self.inner.is_empty()
150 }
151
152 /// Borrow the front message without removing it.
153 ///
154 /// # Examples
155 ///
156 /// ```
157 /// use dynomite::msg::{Msg, MsgQueue, MsgType};
158 ///
159 /// let mut q = MsgQueue::new();
160 /// q.push_back(Msg::new(42, MsgType::ReqRedisGet, true));
161 /// assert_eq!(q.front().unwrap().id(), 42);
162 /// ```
163 #[must_use]
164 pub fn front(&self) -> Option<&Msg> {
165 self.inner.front()
166 }
167
168 /// Mutably borrow the front message.
169 ///
170 /// # Examples
171 ///
172 /// ```
173 /// use dynomite::msg::{Msg, MsgQueue, MsgType};
174 ///
175 /// let mut q = MsgQueue::new();
176 /// q.push_back(Msg::new(1, MsgType::ReqRedisGet, true));
177 /// q.front_mut().unwrap().set_swallow(true);
178 /// ```
179 pub fn front_mut(&mut self) -> Option<&mut Msg> {
180 self.inner.front_mut()
181 }
182
183 /// Iterate over the queue in front-to-back order.
184 ///
185 /// # Examples
186 ///
187 /// ```
188 /// use dynomite::msg::{Msg, MsgQueue, MsgType};
189 ///
190 /// let mut q = MsgQueue::new();
191 /// q.push_back(Msg::new(1, MsgType::ReqRedisGet, true));
192 /// q.push_back(Msg::new(2, MsgType::ReqRedisSet, true));
193 /// let ids: Vec<u64> = q.iter().map(|m| m.id()).collect();
194 /// assert_eq!(ids, vec![1, 2]);
195 /// ```
196 pub fn iter(&self) -> std::collections::vec_deque::Iter<'_, Msg> {
197 self.inner.iter()
198 }
199
200 /// Find a message by id and return a shared reference.
201 ///
202 /// This is the queue-side counterpart to the C
203 /// `dict_msg_id`-shaped lookup that traverses the outstanding-msg
204 /// list. Use [`crate::msg::index::MsgIndex`] when an `O(1)`
205 /// lookup is required across many queues.
206 ///
207 /// # Examples
208 ///
209 /// ```
210 /// use dynomite::msg::{Msg, MsgQueue, MsgType};
211 ///
212 /// let mut q = MsgQueue::new();
213 /// q.push_back(Msg::new(99, MsgType::ReqRedisGet, true));
214 /// assert!(q.msg_get_id_lookup(99).is_some());
215 /// assert!(q.msg_get_id_lookup(1).is_none());
216 /// ```
217 #[must_use]
218 pub fn msg_get_id_lookup(&self, id: MsgId) -> Option<&Msg> {
219 self.inner.iter().find(|m| m.id() == id)
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226 use crate::msg::{Msg, MsgType};
227
228 #[test]
229 fn fifo_order() {
230 let mut q = MsgQueue::new();
231 for i in 1..=3 {
232 q.push_back(Msg::new(i, MsgType::ReqRedisGet, true));
233 }
234 assert_eq!(q.pop_front().unwrap().id(), 1);
235 assert_eq!(q.pop_front().unwrap().id(), 2);
236 assert_eq!(q.pop_front().unwrap().id(), 3);
237 assert!(q.is_empty());
238 }
239
240 #[test]
241 fn lookup_finds_by_id() {
242 let mut q = MsgQueue::new();
243 q.push_back(Msg::new(11, MsgType::ReqRedisGet, true));
244 q.push_back(Msg::new(22, MsgType::ReqRedisSet, true));
245 assert_eq!(q.msg_get_id_lookup(22).unwrap().id(), 22);
246 assert!(q.msg_get_id_lookup(33).is_none());
247 }
248
249 #[test]
250 fn with_capacity_starts_empty() {
251 // with_capacity reserves storage but holds no messages.
252 let q = MsgQueue::with_capacity(8);
253 assert!(q.is_empty());
254 assert_eq!(q.len(), 0);
255 }
256
257 #[test]
258 fn push_front_and_pop_back_are_lifo_ends() {
259 // push_front prepends; pop_back removes the tail.
260 let mut q = MsgQueue::new();
261 q.push_back(Msg::new(1, MsgType::ReqRedisGet, true));
262 q.push_front(Msg::new(0, MsgType::ReqRedisGet, true));
263 // Order is now [0, 1].
264 assert_eq!(q.front().unwrap().id(), 0);
265 assert_eq!(q.pop_back().unwrap().id(), 1);
266 assert_eq!(q.pop_back().unwrap().id(), 0);
267 assert!(q.pop_back().is_none());
268 }
269
270 #[test]
271 fn front_mut_mutates_head_in_place() {
272 // front_mut yields a mutable borrow of the head.
273 let mut q = MsgQueue::new();
274 q.push_back(Msg::new(5, MsgType::ReqRedisGet, true));
275 q.front_mut().unwrap().set_swallow(true);
276 assert!(q.front().unwrap().flags().swallow);
277 // front_mut on an empty queue is None.
278 let mut empty = MsgQueue::new();
279 assert!(empty.front_mut().is_none());
280 }
281
282 #[test]
283 fn into_iter_for_reference_yields_front_to_back() {
284 // The &MsgQueue IntoIterator impl walks in queue order.
285 let mut q = MsgQueue::new();
286 q.push_back(Msg::new(1, MsgType::ReqRedisGet, true));
287 q.push_back(Msg::new(2, MsgType::ReqRedisSet, true));
288 let ids: Vec<u64> = (&q).into_iter().map(Msg::id).collect();
289 assert_eq!(ids, vec![1, 2]);
290 }
291}