Skip to main content

rusmes_storage/
modseq.rs

1//! MODSEQ (Modification Sequence) tracking for IMAP CONDSTORE
2//!
3//! This module implements modification sequence numbers (MODSEQ) for tracking
4//! message and mailbox changes, as required by RFC 7162 (CONDSTORE/QRESYNC).
5
6use std::sync::atomic::{AtomicU64, Ordering};
7use std::sync::Arc;
8
9/// Modification sequence number
10///
11/// MODSEQ is a 64-bit unsigned integer that increases monotonically
12/// for each change to a mailbox or message.
13#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub struct ModSeq(pub u64);
15
16impl ModSeq {
17    /// Create a new MODSEQ with value 0 (invalid/initial state)
18    pub const fn zero() -> Self {
19        Self(0)
20    }
21
22    /// Create a new MODSEQ with value 1 (first valid MODSEQ)
23    pub const fn one() -> Self {
24        Self(1)
25    }
26
27    /// Create a new MODSEQ from a u64 value
28    pub const fn new(value: u64) -> Self {
29        Self(value)
30    }
31
32    /// Get the u64 value
33    pub const fn value(&self) -> u64 {
34        self.0
35    }
36
37    /// Check if this is a valid MODSEQ (non-zero)
38    pub const fn is_valid(&self) -> bool {
39        self.0 > 0
40    }
41
42    /// Increment and return the new value
43    pub fn increment(&mut self) -> Self {
44        self.0 += 1;
45        *self
46    }
47}
48
49impl std::fmt::Display for ModSeq {
50    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51        write!(f, "{}", self.0)
52    }
53}
54
55impl From<u64> for ModSeq {
56    fn from(value: u64) -> Self {
57        Self(value)
58    }
59}
60
61impl From<ModSeq> for u64 {
62    fn from(modseq: ModSeq) -> u64 {
63        modseq.0
64    }
65}
66
67/// MODSEQ generator for creating unique modification sequence numbers
68///
69/// This is a thread-safe atomic counter that ensures MODSEQs are
70/// monotonically increasing across the entire server.
71#[derive(Debug, Clone)]
72pub struct ModSeqGenerator {
73    counter: Arc<AtomicU64>,
74}
75
76impl ModSeqGenerator {
77    /// Create a new MODSEQ generator starting from 1
78    pub fn new() -> Self {
79        Self {
80            counter: Arc::new(AtomicU64::new(1)),
81        }
82    }
83
84    /// Create a new MODSEQ generator starting from a specific value
85    pub fn with_start_value(start: u64) -> Self {
86        Self {
87            counter: Arc::new(AtomicU64::new(start.max(1))),
88        }
89    }
90
91    /// Generate the next MODSEQ value
92    pub fn next(&self) -> ModSeq {
93        let value = self.counter.fetch_add(1, Ordering::SeqCst);
94        ModSeq(value)
95    }
96
97    /// Get the current MODSEQ value without incrementing
98    pub fn current(&self) -> ModSeq {
99        let value = self.counter.load(Ordering::SeqCst);
100        ModSeq(value.saturating_sub(1).max(1))
101    }
102
103    /// Get the highest MODSEQ value (next - 1)
104    pub fn highest(&self) -> ModSeq {
105        self.current()
106    }
107}
108
109impl Default for ModSeqGenerator {
110    fn default() -> Self {
111        Self::new()
112    }
113}
114
115/// Message metadata with MODSEQ tracking
116#[derive(Debug, Clone)]
117pub struct MessageModSeq {
118    /// Message UID
119    pub uid: u32,
120    /// Current MODSEQ value for this message
121    pub modseq: ModSeq,
122}
123
124impl MessageModSeq {
125    /// Create new message metadata
126    pub fn new(uid: u32, modseq: ModSeq) -> Self {
127        Self { uid, modseq }
128    }
129}
130
131/// Mailbox metadata with MODSEQ tracking
132#[derive(Debug, Clone)]
133pub struct MailboxModSeq {
134    /// Mailbox name
135    pub name: String,
136    /// Highest MODSEQ in this mailbox
137    pub highestmodseq: ModSeq,
138    /// UIDVALIDITY value
139    pub uidvalidity: u32,
140    /// Next UID to be assigned
141    pub uidnext: u32,
142}
143
144impl MailboxModSeq {
145    /// Create new mailbox metadata
146    pub fn new(name: String, uidvalidity: u32, uidnext: u32) -> Self {
147        Self {
148            name,
149            highestmodseq: ModSeq::one(),
150            uidvalidity,
151            uidnext,
152        }
153    }
154
155    /// Update the highest MODSEQ if the given value is higher
156    pub fn update_modseq(&mut self, modseq: ModSeq) {
157        if modseq > self.highestmodseq {
158            self.highestmodseq = modseq;
159        }
160    }
161}
162
163#[cfg(test)]
164mod tests {
165    use super::*;
166    use std::collections::HashSet;
167
168    #[test]
169    fn test_modseq_creation() {
170        let modseq = ModSeq::zero();
171        assert_eq!(modseq.value(), 0);
172        assert!(!modseq.is_valid());
173
174        let modseq = ModSeq::one();
175        assert_eq!(modseq.value(), 1);
176        assert!(modseq.is_valid());
177
178        let modseq = ModSeq::new(42);
179        assert_eq!(modseq.value(), 42);
180        assert!(modseq.is_valid());
181    }
182
183    #[test]
184    fn test_modseq_increment() {
185        let mut modseq = ModSeq::one();
186        assert_eq!(modseq.value(), 1);
187
188        let new_modseq = modseq.increment();
189        assert_eq!(new_modseq.value(), 2);
190        assert_eq!(modseq.value(), 2);
191    }
192
193    #[test]
194    fn test_modseq_increment_multiple() {
195        let mut modseq = ModSeq::zero();
196        for i in 1..=10 {
197            let result = modseq.increment();
198            assert_eq!(result.value(), i);
199            assert_eq!(modseq.value(), i);
200        }
201    }
202
203    #[test]
204    fn test_modseq_ordering() {
205        let modseq1 = ModSeq::new(10);
206        let modseq2 = ModSeq::new(20);
207
208        assert!(modseq1 < modseq2);
209        assert!(modseq2 > modseq1);
210        assert_eq!(modseq1, modseq1.clone());
211    }
212
213    #[test]
214    fn test_modseq_ordering_edge_cases() {
215        let zero = ModSeq::zero();
216        let one = ModSeq::one();
217        let max = ModSeq::new(u64::MAX);
218
219        assert!(zero < one);
220        assert!(one < max);
221        assert!(zero < max);
222    }
223
224    #[test]
225    fn test_modseq_equality() {
226        let m1 = ModSeq::new(100);
227        let m2 = ModSeq::new(100);
228        let m3 = ModSeq::new(200);
229
230        assert_eq!(m1, m2);
231        assert_ne!(m1, m3);
232    }
233
234    #[test]
235    fn test_modseq_hash() {
236        let mut set = HashSet::new();
237        set.insert(ModSeq::new(1));
238        set.insert(ModSeq::new(2));
239        set.insert(ModSeq::new(1)); // Duplicate
240
241        assert_eq!(set.len(), 2);
242        assert!(set.contains(&ModSeq::new(1)));
243        assert!(set.contains(&ModSeq::new(2)));
244    }
245
246    #[test]
247    fn test_modseq_generator() {
248        let gen = ModSeqGenerator::new();
249
250        let modseq1 = gen.next();
251        assert_eq!(modseq1.value(), 1);
252
253        let modseq2 = gen.next();
254        assert_eq!(modseq2.value(), 2);
255
256        let modseq3 = gen.next();
257        assert_eq!(modseq3.value(), 3);
258    }
259
260    #[test]
261    fn test_modseq_generator_with_start() {
262        let gen = ModSeqGenerator::with_start_value(100);
263
264        let modseq1 = gen.next();
265        assert_eq!(modseq1.value(), 100);
266
267        let modseq2 = gen.next();
268        assert_eq!(modseq2.value(), 101);
269    }
270
271    #[test]
272    fn test_modseq_generator_with_zero_start() {
273        // Zero should be converted to 1
274        let gen = ModSeqGenerator::with_start_value(0);
275        let modseq = gen.next();
276        assert_eq!(modseq.value(), 1);
277    }
278
279    #[test]
280    fn test_modseq_generator_current() {
281        let gen = ModSeqGenerator::new();
282
283        let _m1 = gen.next();
284        let _m2 = gen.next();
285        let _m3 = gen.next();
286
287        let current = gen.current();
288        assert_eq!(current.value(), 3);
289
290        let highest = gen.highest();
291        assert_eq!(highest, current);
292    }
293
294    #[test]
295    fn test_modseq_generator_current_before_first_next() {
296        let gen = ModSeqGenerator::new();
297        let current = gen.current();
298        assert_eq!(current.value(), 1);
299    }
300
301    #[test]
302    fn test_modseq_generator_clone() {
303        let gen1 = ModSeqGenerator::new();
304        let _m1 = gen1.next();
305        let _m2 = gen1.next();
306
307        // Cloning shares the same Arc<AtomicU64>, so both generators use the same counter
308        let gen2 = gen1.clone();
309        let m3 = gen2.next();
310        let m4 = gen1.next();
311
312        assert_eq!(m3.value(), 3);
313        assert_eq!(m4.value(), 4); // gen1 continues from where gen2 left off
314    }
315
316    #[test]
317    fn test_modseq_generator_thread_safety() {
318        use std::thread;
319
320        let gen = ModSeqGenerator::new();
321        let gen1 = gen.clone();
322        let gen2 = gen.clone();
323
324        let handle1 = thread::spawn(move || {
325            let mut values = Vec::new();
326            for _ in 0..10 {
327                values.push(gen1.next().value());
328            }
329            values
330        });
331
332        let handle2 = thread::spawn(move || {
333            let mut values = Vec::new();
334            for _ in 0..10 {
335                values.push(gen2.next().value());
336            }
337            values
338        });
339
340        let values1 = handle1.join().unwrap();
341        let values2 = handle2.join().unwrap();
342
343        // All values should be unique
344        let mut all_values: Vec<_> = values1.into_iter().chain(values2).collect();
345        all_values.sort_unstable();
346        all_values.dedup();
347        assert_eq!(all_values.len(), 20);
348    }
349
350    #[test]
351    fn test_message_modseq() {
352        let msg = MessageModSeq::new(42, ModSeq::new(100));
353        assert_eq!(msg.uid, 42);
354        assert_eq!(msg.modseq.value(), 100);
355    }
356
357    #[test]
358    fn test_message_modseq_clone() {
359        let msg1 = MessageModSeq::new(42, ModSeq::new(100));
360        let msg2 = msg1.clone();
361        assert_eq!(msg1.uid, msg2.uid);
362        assert_eq!(msg1.modseq, msg2.modseq);
363    }
364
365    #[test]
366    fn test_mailbox_modseq_new() {
367        let mbox = MailboxModSeq::new("INBOX".to_string(), 123, 456);
368        assert_eq!(mbox.name, "INBOX");
369        assert_eq!(mbox.highestmodseq, ModSeq::one());
370        assert_eq!(mbox.uidvalidity, 123);
371        assert_eq!(mbox.uidnext, 456);
372    }
373
374    #[test]
375    fn test_mailbox_modseq_update() {
376        let mut mbox = MailboxModSeq::new("INBOX".to_string(), 1, 1);
377        assert_eq!(mbox.highestmodseq.value(), 1);
378
379        mbox.update_modseq(ModSeq::new(10));
380        assert_eq!(mbox.highestmodseq.value(), 10);
381
382        // Should not decrease
383        mbox.update_modseq(ModSeq::new(5));
384        assert_eq!(mbox.highestmodseq.value(), 10);
385
386        // Should increase
387        mbox.update_modseq(ModSeq::new(15));
388        assert_eq!(mbox.highestmodseq.value(), 15);
389    }
390
391    #[test]
392    fn test_mailbox_modseq_update_same_value() {
393        let mut mbox = MailboxModSeq::new("INBOX".to_string(), 1, 1);
394        mbox.update_modseq(ModSeq::new(10));
395        assert_eq!(mbox.highestmodseq.value(), 10);
396
397        // Updating with same value should not change anything
398        mbox.update_modseq(ModSeq::new(10));
399        assert_eq!(mbox.highestmodseq.value(), 10);
400    }
401
402    #[test]
403    fn test_mailbox_modseq_clone() {
404        let mbox1 = MailboxModSeq::new("INBOX".to_string(), 123, 456);
405        let mbox2 = mbox1.clone();
406        assert_eq!(mbox1.name, mbox2.name);
407        assert_eq!(mbox1.highestmodseq, mbox2.highestmodseq);
408        assert_eq!(mbox1.uidvalidity, mbox2.uidvalidity);
409        assert_eq!(mbox1.uidnext, mbox2.uidnext);
410    }
411
412    #[test]
413    fn test_modseq_from_u64() {
414        let modseq: ModSeq = 42u64.into();
415        assert_eq!(modseq.value(), 42);
416
417        let value: u64 = modseq.into();
418        assert_eq!(value, 42);
419    }
420
421    #[test]
422    fn test_modseq_from_u64_max() {
423        let modseq: ModSeq = u64::MAX.into();
424        assert_eq!(modseq.value(), u64::MAX);
425    }
426
427    #[test]
428    fn test_modseq_display() {
429        let modseq = ModSeq::new(12345);
430        assert_eq!(modseq.to_string(), "12345");
431    }
432
433    #[test]
434    fn test_modseq_display_zero() {
435        let modseq = ModSeq::zero();
436        assert_eq!(modseq.to_string(), "0");
437    }
438
439    #[test]
440    fn test_modseq_display_large() {
441        let modseq = ModSeq::new(u64::MAX);
442        assert_eq!(modseq.to_string(), u64::MAX.to_string());
443    }
444
445    #[test]
446    fn test_modseq_generator_default() {
447        let gen = ModSeqGenerator::default();
448        let modseq = gen.next();
449        assert_eq!(modseq.value(), 1);
450    }
451
452    #[test]
453    fn test_modseq_const_functions() {
454        const ZERO: ModSeq = ModSeq::zero();
455        const ONE: ModSeq = ModSeq::one();
456        const CUSTOM: ModSeq = ModSeq::new(42);
457
458        assert_eq!(ZERO.value(), 0);
459        assert_eq!(ONE.value(), 1);
460        assert_eq!(CUSTOM.value(), 42);
461    }
462}