Skip to main content

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}