• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

duesee / imap-codec / 21712483783

05 Feb 2026 01:01PM UTC coverage: 91.061% (-0.1%) from 91.203%
21712483783

Pull #691

github

web-flow
Merge 84684c429 into 790307d16
Pull Request #691: feat: add quirk to dedup and sort sequence sets during encoding

130 of 162 new or added lines in 2 files covered. (80.25%)

32 existing lines in 1 file now uncovered.

10503 of 11534 relevant lines covered (91.06%)

919.31 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.17
/imap-types/src/sequence.rs
1
use std::{
2
    cmp::{Ordering, max},
3
    collections::{HashSet, VecDeque},
4
    fmt::Debug,
5
    iter::Rev,
6
    mem,
7
    num::NonZeroU32,
8
    ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive},
9
    str::FromStr,
10
};
11

12
#[cfg(feature = "arbitrary")]
13
use arbitrary::Arbitrary;
14
use bounded_static_derive::ToStatic;
15
#[cfg(feature = "serde")]
16
use serde::{Deserialize, Serialize};
17

18
use crate::{
19
    core::Vec1,
20
    error::{ValidationError, ValidationErrorKind},
21
};
22

23
pub const ONE: NonZeroU32 = match NonZeroU32::new(1) {
24
    Some(one) => one,
25
    None => panic!(),
26
};
27
pub const MIN: NonZeroU32 = ONE;
28
pub const MAX: NonZeroU32 = match NonZeroU32::new(u32::MAX) {
29
    Some(max) => max,
30
    None => panic!(),
31
};
32

33
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
34
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
35
#[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)]
36
pub struct SequenceSet(pub Vec1<Sequence>);
37

38
impl SequenceSet {
39
    pub fn normalize(&mut self) -> &mut Self {
243✔
40
        let mut singles = HashSet::with_capacity(self.0.0.len());
243✔
41
        let mut ranges = Vec::with_capacity(self.0.0.len());
243✔
42

43
        for seq in &mut self.0.0 {
281✔
44
            match seq.normalize() {
281✔
45
                Sequence::Single(id) => {
138✔
46
                    singles.insert(id.to_non_zero_u32());
138✔
47
                    ranges.push(id.to_non_zero_u32()..id.to_non_zero_u32().saturating_add(1));
138✔
48
                }
138✔
49
                Sequence::Range(a, b) => {
143✔
50
                    ranges.push(a.to_non_zero_u32()..b.to_non_zero_u32().saturating_add(1));
143✔
51
                }
143✔
52
            }
53
        }
54

55
        self.0.0.clear();
243✔
56

57
        for single in singles.clone() {
255✔
58
            for range in &mut ranges {
224✔
59
                if single.get() == range.start.get().max(2) - 1 {
224✔
60
                    singles.remove(&single);
86✔
61
                    range.start = single;
86✔
62
                } else if single.get() == range.end.get() {
138✔
63
                    singles.remove(&single);
31✔
64
                    range.end = single;
31✔
65
                } else if range.contains(&single) {
107✔
66
                    singles.remove(&single);
69✔
67
                }
77✔
68
            }
69
        }
70

71
        for range in merge_ranges(ranges) {
253✔
72
            singles.retain(|single| !range.contains(single));
253✔
73

74
            match (range.start, range.end) {
253✔
75
                (a, b) if a == b || a.saturating_add(1) == b => {
253✔
76
                    let a = NonZeroU32::new(a.get()).unwrap();
108✔
77
                    self.0.0.push(Sequence::Single(a.into()));
108✔
78
                }
108✔
79
                (a, NonZeroU32::MAX) => {
26✔
80
                    self.0.0.push(Sequence::Range(a.into(), SeqOrUid::Asterisk))
26✔
81
                }
82
                (a, b) => {
119✔
83
                    let b = NonZeroU32::new(b.get().max(2) - 1).unwrap();
119✔
84
                    self.0.0.push(Sequence::Range(a.into(), b.into()));
119✔
85
                }
119✔
86
            }
87
        }
88

89
        for single in singles {
243✔
NEW
90
            self.0.0.push(single.into());
×
NEW
91
        }
×
92

93
        self.0.0.sort_by_key(|seq| match seq {
253✔
94
            Sequence::Single(a) => a.to_non_zero_u32(),
4✔
95
            Sequence::Range(a, _) => a.to_non_zero_u32(),
16✔
96
        });
20✔
97

98
        self
243✔
99
    }
243✔
100
}
101

102
impl From<Sequence> for SequenceSet {
103
    fn from(sequence: Sequence) -> Self {
1,063✔
104
        Self(Vec1::from(sequence))
1,063✔
105
    }
1,063✔
106
}
107

108
macro_rules! impl_from_t_for_sequence_set {
109
    ($thing:ty) => {
110
        impl From<$thing> for SequenceSet {
111
            fn from(value: $thing) -> Self {
208✔
112
                Self::from(Sequence::from(value))
208✔
113
            }
208✔
114
        }
115
    };
116
}
117

118
macro_rules! impl_try_from_t_for_sequence_set {
119
    ($thing:ty) => {
120
        impl TryFrom<$thing> for SequenceSet {
121
            type Error = ValidationError;
122

123
            fn try_from(value: $thing) -> Result<Self, Self::Error> {
26✔
124
                Ok(Self::from(Sequence::try_from(value)?))
26✔
125
            }
26✔
126
        }
127
    };
128
}
129

130
impl_from_t_for_sequence_set!(SeqOrUid);
131
impl_from_t_for_sequence_set!(NonZeroU32);
132
impl_from_t_for_sequence_set!(RangeFull);
133
impl_from_t_for_sequence_set!(RangeFrom<NonZeroU32>);
134
impl_try_from_t_for_sequence_set!(RangeTo<NonZeroU32>);
135
impl_from_t_for_sequence_set!(RangeToInclusive<NonZeroU32>);
136
impl_try_from_t_for_sequence_set!(Range<NonZeroU32>);
137
impl_from_t_for_sequence_set!(RangeInclusive<NonZeroU32>);
138

139
// `SequenceSet::try_from` implementations.
140

141
impl TryFrom<Vec<Sequence>> for SequenceSet {
142
    type Error = ValidationError;
143

UNCOV
144
    fn try_from(sequences: Vec<Sequence>) -> Result<Self, Self::Error> {
×
UNCOV
145
        Ok(Self(Vec1::try_from(sequences).map_err(|_| {
×
UNCOV
146
            ValidationError::new(ValidationErrorKind::Empty)
×
UNCOV
147
        })?))
×
UNCOV
148
    }
×
149
}
150

151
impl TryFrom<Vec<NonZeroU32>> for SequenceSet {
152
    type Error = ValidationError;
153

154
    fn try_from(sequences: Vec<NonZeroU32>) -> Result<Self, Self::Error> {
×
155
        Ok(Self(
UNCOV
156
            Vec1::try_from(
×
UNCOV
157
                sequences
×
UNCOV
158
                    .into_iter()
×
UNCOV
159
                    .map(Sequence::from)
×
UNCOV
160
                    .collect::<Vec<_>>(),
×
161
            )
UNCOV
162
            .map_err(|_| ValidationError::new(ValidationErrorKind::Empty))?,
×
163
        ))
164
    }
×
165
}
166

167
impl TryFrom<&[NonZeroU32]> for SequenceSet {
168
    type Error = ValidationError;
169

UNCOV
170
    fn try_from(value: &[NonZeroU32]) -> Result<Self, Self::Error> {
×
171
        SequenceSet::try_from(value.to_vec())
×
UNCOV
172
    }
×
173
}
174

175
impl TryFrom<&str> for SequenceSet {
176
    type Error = ValidationError;
177

178
    fn try_from(value: &str) -> Result<Self, Self::Error> {
244✔
179
        value.parse()
244✔
180
    }
244✔
181
}
182

183
impl FromStr for SequenceSet {
184
    type Err = ValidationError;
185

186
    fn from_str(value: &str) -> Result<Self, Self::Err> {
244✔
187
        let mut results = vec![];
244✔
188

189
        for seq in value.split(',') {
354✔
190
            results.push(Sequence::try_from(seq)?);
354✔
191
        }
192

193
        Ok(SequenceSet(Vec1::try_from(results).map_err(|_| {
210✔
UNCOV
194
            ValidationError::new(ValidationErrorKind::Empty)
×
UNCOV
195
        })?))
×
196
    }
244✔
197
}
198

199
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
200
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
201
#[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))]
202
#[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)]
203
pub enum Sequence {
204
    Single(SeqOrUid),
205
    Range(SeqOrUid, SeqOrUid),
206
}
207

208
impl Sequence {
209
    pub fn normalize(&mut self) -> &mut Self {
553✔
210
        match self {
281✔
211
            Sequence::Range(a, b) if *a == *b => {
283✔
212
                let range = Sequence::Single(a.clone().into());
2✔
213
                let _ = mem::replace(self, range);
2✔
214
            }
2✔
215
            Sequence::Range(a, b) if *a > *b => {
281✔
216
                mem::swap(a, b);
22✔
217
            }
22✔
218
            _ => {
529✔
219
                // already normalized
529✔
220
            }
529✔
221
        };
222

223
        self
553✔
224
    }
553✔
225
}
226

227
impl PartialOrd for Sequence {
NEW
228
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
×
NEW
229
        match (self, other) {
×
NEW
230
            (Sequence::Single(a), Sequence::Single(b)) => a.partial_cmp(b),
×
NEW
231
            (Sequence::Single(a), Sequence::Range(b1, b2)) => {
×
NEW
232
                if a < b1.min(b2) {
×
NEW
233
                    return Some(Ordering::Less);
×
NEW
234
                }
×
235

NEW
236
                if a > b1.max(b2) {
×
NEW
237
                    return Some(Ordering::Greater);
×
NEW
238
                }
×
239

NEW
240
                None
×
241
            }
NEW
242
            (Sequence::Range(a1, a2), Sequence::Single(b)) => {
×
NEW
243
                if b < a1.min(a2) {
×
NEW
244
                    return Some(Ordering::Greater);
×
NEW
245
                }
×
246

NEW
247
                if b > a1.max(a2) {
×
NEW
248
                    return Some(Ordering::Less);
×
NEW
249
                }
×
250

NEW
251
                None
×
252
            }
NEW
253
            (Sequence::Range(a1, a2), Sequence::Range(b1, b2)) => {
×
NEW
254
                if a1.max(a2) < b1 && a1.max(a2) < b2 {
×
NEW
255
                    return Some(Ordering::Less);
×
NEW
256
                }
×
257

NEW
258
                if a1.min(a2) > b1 && a1.min(a2) > b2 {
×
NEW
259
                    return Some(Ordering::Greater);
×
NEW
260
                }
×
261

NEW
262
                None
×
263
            }
264
        }
NEW
265
    }
×
266
}
267

268
impl From<SeqOrUid> for Sequence {
269
    fn from(value: SeqOrUid) -> Self {
556✔
270
        Self::Single(value)
556✔
271
    }
556✔
272
}
273

274
impl From<NonZeroU32> for Sequence {
275
    fn from(value: NonZeroU32) -> Self {
52✔
276
        Self::Single(SeqOrUid::from(value))
52✔
277
    }
52✔
278
}
279

280
impl TryFrom<&str> for Sequence {
281
    type Error = ValidationError;
282

283
    fn try_from(value: &str) -> Result<Self, Self::Error> {
378✔
284
        value.parse()
378✔
285
    }
378✔
286
}
287

288
impl FromStr for Sequence {
289
    type Err = ValidationError;
290

291
    fn from_str(value: &str) -> Result<Self, Self::Err> {
378✔
292
        match value.split(':').count() {
378✔
UNCOV
293
            0 => Err(ValidationError::new(ValidationErrorKind::Empty)),
×
294
            1 => Ok(Sequence::Single(SeqOrUid::try_from(value)?)),
216✔
295
            2 => {
296
                let mut split = value.split(':');
160✔
297

298
                let start = split.next().unwrap();
160✔
299
                let end = split.next().unwrap();
160✔
300

301
                Ok(Sequence::Range(
302
                    SeqOrUid::try_from(start)?,
160✔
303
                    SeqOrUid::try_from(end)?,
156✔
304
                ))
305
            }
306
            _ => Err(ValidationError::new(ValidationErrorKind::Invalid)),
2✔
307
        }
308
    }
378✔
309
}
310

311
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
312
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
313
#[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))]
314
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, ToStatic)]
315
pub enum SeqOrUid {
316
    Value(NonZeroU32),
317
    Asterisk,
318
}
319

320
impl SeqOrUid {
321
    pub fn to_non_zero_u32(&self) -> NonZeroU32 {
720✔
322
        match self {
720✔
323
            SeqOrUid::Value(n) => *n,
655✔
324
            SeqOrUid::Asterisk => NonZeroU32::MAX,
65✔
325
        }
326
    }
720✔
327
}
328

329
impl Ord for SeqOrUid {
330
    fn cmp(&self, other: &Self) -> Ordering {
281✔
331
        match (self, other) {
281✔
332
            (SeqOrUid::Value(a), SeqOrUid::Value(b)) => a.cmp(b),
212✔
NEW
333
            (SeqOrUid::Asterisk, SeqOrUid::Asterisk) => Ordering::Equal,
×
334
            (_, SeqOrUid::Asterisk) => Ordering::Less,
67✔
335
            (SeqOrUid::Asterisk, _) => Ordering::Greater,
2✔
336
        }
337
    }
281✔
338
}
339

340
impl PartialOrd for SeqOrUid {
341
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
281✔
342
        Some(self.cmp(other))
281✔
343
    }
281✔
344
}
345

346
impl From<NonZeroU32> for SeqOrUid {
347
    fn from(value: NonZeroU32) -> Self {
1,236✔
348
        if value == NonZeroU32::MAX {
1,236✔
349
            Self::Asterisk
104✔
350
        } else {
351
            Self::Value(value)
1,132✔
352
        }
353
    }
1,236✔
354
}
355

356
macro_rules! impl_try_from_num {
357
    ($num:ty) => {
358
        impl TryFrom<&[$num]> for SequenceSet {
359
            type Error = ValidationError;
360

UNCOV
361
            fn try_from(values: &[$num]) -> Result<Self, Self::Error> {
×
UNCOV
362
                let mut checked = Vec::new();
×
363

UNCOV
364
                for value in values {
×
UNCOV
365
                    checked.push(Sequence::try_from(*value)?);
×
366
                }
367

368
                Self::try_from(checked)
×
369
            }
×
370
        }
371

372
        impl TryFrom<$num> for SequenceSet {
373
            type Error = ValidationError;
374

375
            fn try_from(value: $num) -> Result<Self, Self::Error> {
454✔
376
                Ok(Self::from(Sequence::try_from(value)?))
454✔
377
            }
454✔
378
        }
379

380
        impl TryFrom<$num> for Sequence {
381
            type Error = ValidationError;
382

383
            fn try_from(value: $num) -> Result<Self, Self::Error> {
870✔
384
                Ok(Self::from(SeqOrUid::try_from(value)?))
870✔
385
            }
870✔
386
        }
387

388
        impl TryFrom<$num> for SeqOrUid {
389
            type Error = ValidationError;
390

391
            fn try_from(value: $num) -> Result<Self, Self::Error> {
3,967✔
392
                #[allow(irrefutable_let_patterns)]
393
                if let Ok(value) = u32::try_from(value) {
3,967✔
394
                    if let Ok(value) = NonZeroU32::try_from(value) {
3,733✔
395
                        return Ok(Self::Value(value));
2,561✔
396
                    }
1,172✔
397
                }
234✔
398

399
                Err(ValidationError::new(ValidationErrorKind::Invalid))
1,406✔
400
            }
3,967✔
401
        }
402
    };
403
}
404

405
impl_try_from_num!(i8);
406
impl_try_from_num!(i16);
407
impl_try_from_num!(i32);
408
impl_try_from_num!(i64);
409
impl_try_from_num!(isize);
410
impl_try_from_num!(u8);
411
impl_try_from_num!(u16);
412
impl_try_from_num!(u32);
413
impl_try_from_num!(u64);
414
impl_try_from_num!(usize);
415

416
impl TryFrom<&str> for SeqOrUid {
417
    type Error = ValidationError;
418

419
    fn try_from(value: &str) -> Result<Self, Self::Error> {
532✔
420
        value.parse()
532✔
421
    }
532✔
422
}
423

424
impl FromStr for SeqOrUid {
425
    type Err = ValidationError;
426

427
    fn from_str(value: &str) -> Result<Self, Self::Err> {
532✔
428
        if value == "*" {
532✔
429
            Ok(SeqOrUid::Asterisk)
82✔
430
        } else {
431
            // This is to align parsing here with the IMAP grammar:
432
            // Rust's `parse::<NonZeroU32>` function accepts numbers that start with 0.
433
            // For example, 00001, is interpreted as 1. But this is not allowed in IMAP.
434
            if value.starts_with('0') {
450✔
435
                Err(ValidationError::new(ValidationErrorKind::Invalid))
4✔
436
            } else {
437
                Ok(SeqOrUid::Value(NonZeroU32::from_str(value).map_err(
446✔
438
                    |_| ValidationError::new(ValidationErrorKind::Invalid),
28✔
439
                )?))
28✔
440
            }
441
        }
442
    }
532✔
443
}
444

445
// -------------------------------------------------------------------------------------------------
446

447
macro_rules! impl_try_from_num_range {
448
    ($num:ty) => {
449
        impl TryFrom<RangeFrom<$num>> for SequenceSet {
450
            type Error = ValidationError;
451

452
            fn try_from(range: RangeFrom<$num>) -> Result<Self, Self::Error> {
260✔
453
                Ok(Self::from(Sequence::try_from(range)?))
260✔
454
            }
260✔
455
        }
456

457
        impl TryFrom<RangeTo<$num>> for SequenceSet {
458
            type Error = ValidationError;
459

460
            fn try_from(range: RangeTo<$num>) -> Result<Self, Self::Error> {
156✔
461
                Ok(Self::from(Sequence::try_from(range)?))
156✔
462
            }
156✔
463
        }
464

465
        impl TryFrom<RangeToInclusive<$num>> for SequenceSet {
466
            type Error = ValidationError;
467

UNCOV
468
            fn try_from(range: RangeToInclusive<$num>) -> Result<Self, Self::Error> {
×
UNCOV
469
                Ok(Self::from(Sequence::try_from(range)?))
×
UNCOV
470
            }
×
471
        }
472

473
        impl TryFrom<Range<$num>> for SequenceSet {
474
            type Error = ValidationError;
475

476
            fn try_from(range: Range<$num>) -> Result<Self, Self::Error> {
572✔
477
                Ok(Self::from(Sequence::try_from(range)?))
572✔
478
            }
572✔
479
        }
480

481
        impl TryFrom<RangeInclusive<$num>> for SequenceSet {
482
            type Error = ValidationError;
483

UNCOV
484
            fn try_from(range: RangeInclusive<$num>) -> Result<Self, Self::Error> {
×
UNCOV
485
                Ok(Self::from(Sequence::try_from(range)?))
×
UNCOV
486
            }
×
487
        }
488

489
        // -----------------------------------------------------------------------------------------
490

491
        impl TryFrom<RangeFrom<$num>> for Sequence {
492
            type Error = ValidationError;
493

494
            fn try_from(range: RangeFrom<$num>) -> Result<Self, Self::Error> {
537✔
495
                Ok(Self::Range(
496
                    SeqOrUid::try_from(range.start)?,
537✔
497
                    SeqOrUid::Asterisk,
277✔
498
                ))
499
            }
537✔
500
        }
501

502
        impl TryFrom<RangeTo<$num>> for Sequence {
503
            type Error = ValidationError;
504

505
            fn try_from(range: RangeTo<$num>) -> Result<Self, Self::Error> {
314✔
506
                Ok(Self::Range(
507
                    SeqOrUid::from(ONE),
314✔
508
                    SeqOrUid::try_from(range.end.saturating_sub(1))?,
314✔
509
                ))
510
            }
314✔
511
        }
512

513
        impl TryFrom<RangeToInclusive<$num>> for Sequence {
514
            type Error = ValidationError;
515

516
            fn try_from(range: RangeToInclusive<$num>) -> Result<Self, Self::Error> {
2✔
517
                Ok(Self::Range(
518
                    SeqOrUid::from(ONE),
2✔
519
                    SeqOrUid::try_from(range.end)?,
2✔
520
                ))
521
            }
2✔
522
        }
523

524
        impl TryFrom<Range<$num>> for Sequence {
525
            type Error = ValidationError;
526

527
            fn try_from(range: Range<$num>) -> Result<Self, Self::Error> {
1,146✔
528
                Ok(Self::Range(
529
                    SeqOrUid::try_from(range.start)?,
1,146✔
530
                    SeqOrUid::try_from(range.end.saturating_sub(1))?,
626✔
531
                ))
532
            }
1,146✔
533
        }
534

535
        impl TryFrom<RangeInclusive<$num>> for Sequence {
536
            type Error = ValidationError;
537

538
            fn try_from(range: RangeInclusive<$num>) -> Result<Self, Self::Error> {
2✔
539
                Ok(Self::Range(
540
                    SeqOrUid::try_from(*range.start())?,
2✔
541
                    SeqOrUid::try_from(*range.end())?,
2✔
542
                ))
543
            }
2✔
544
        }
545
    };
546
}
547

548
impl_try_from_num_range!(i8);
549
impl_try_from_num_range!(i16);
550
impl_try_from_num_range!(i32);
551
impl_try_from_num_range!(i64);
552
impl_try_from_num_range!(isize);
553
impl_try_from_num_range!(u8);
554
impl_try_from_num_range!(u16);
555
impl_try_from_num_range!(u32);
556
impl_try_from_num_range!(u64);
557
impl_try_from_num_range!(usize);
558

559
impl From<RangeFull> for Sequence {
560
    fn from(_: RangeFull) -> Self {
288✔
561
        Self::from(MIN..)
288✔
562
    }
288✔
563
}
564

565
impl From<RangeFrom<NonZeroU32>> for Sequence {
566
    fn from(range: RangeFrom<NonZeroU32>) -> Self {
314✔
567
        Self::Range(SeqOrUid::from(range.start), SeqOrUid::Asterisk)
314✔
568
    }
314✔
569
}
570

571
impl TryFrom<RangeTo<NonZeroU32>> for Sequence {
572
    type Error = ValidationError;
573

574
    fn try_from(range: RangeTo<NonZeroU32>) -> Result<Self, Self::Error> {
26✔
575
        Self::try_from(MIN..range.end)
26✔
576
    }
26✔
577
}
578

579
impl From<RangeToInclusive<NonZeroU32>> for Sequence {
UNCOV
580
    fn from(range: RangeToInclusive<NonZeroU32>) -> Self {
×
UNCOV
581
        Self::from(MIN..=range.end)
×
UNCOV
582
    }
×
583
}
584

585
impl TryFrom<Range<NonZeroU32>> for Sequence {
586
    type Error = ValidationError;
587

588
    fn try_from(range: Range<NonZeroU32>) -> Result<Self, Self::Error> {
52✔
589
        Ok(Self::Range(
590
            SeqOrUid::from(MIN),
52✔
591
            SeqOrUid::try_from(range.end.get().saturating_sub(1))?,
52✔
592
        ))
593
    }
52✔
594
}
595

596
impl From<RangeInclusive<NonZeroU32>> for Sequence {
597
    fn from(range: RangeInclusive<NonZeroU32>) -> Self {
52✔
598
        Self::Range(SeqOrUid::from(*range.start()), SeqOrUid::from(*range.end()))
52✔
599
    }
52✔
600
}
601

602
// -------------------------------------------------------------------------------------------------
603

604
impl<'a> SequenceSet {
605
    /// Iterate over a sorted, deduplicated set of sequence numbers or UIDs.
606
    ///
607
    /// # Example
608
    ///
609
    /// ```
610
    /// use std::num::NonZeroU32;
611
    ///
612
    /// use imap_types::sequence::SequenceSet;
613
    ///
614
    /// let seq = SequenceSet::try_from("1:5,10:3,10,10,10,1").unwrap();
615
    /// let largest = NonZeroU32::new(10).unwrap();
616
    ///
617
    /// assert_eq!(
618
    ///     seq.iter(largest).collect::<Vec<_>>(),
619
    ///     [1u32, 2, 3, 4, 5, 6, 7, 8, 9, 10]
620
    ///         .into_iter()
621
    ///         .map(|e| NonZeroU32::new(e).unwrap())
622
    ///         .collect::<Vec<_>>()
623
    /// );
624
    /// ```
625
    pub fn iter(&'a self, largest: NonZeroU32) -> impl Iterator<Item = NonZeroU32> + 'a {
22✔
626
        let ranges = simplify(self.clone(), largest, true);
22✔
627
        let ranges = cleanup(ranges);
22✔
628

629
        SequenceSetIter {
22✔
630
            ranges,
22✔
631
            active_range: None,
22✔
632
        }
22✔
633
    }
22✔
634

635
    /// Iterate over the given set of sequence numbers or UIDs.
636
    ///
637
    /// Note: This method expands the sequence set keeping duplicates and ordering.
638
    ///
639
    /// # Example
640
    ///
641
    /// ```
642
    /// use std::num::NonZeroU32;
643
    ///
644
    /// use imap_types::sequence::SequenceSet;
645
    ///
646
    /// let seq = SequenceSet::try_from("1:3,1:3,3:1,1").unwrap();
647
    /// let largest = NonZeroU32::new(10).unwrap();
648
    ///
649
    /// assert_eq!(
650
    ///     seq.iter_naive(largest).collect::<Vec<_>>(),
651
    ///     [1u32, 2, 3, 1, 2, 3, 3, 2, 1, 1]
652
    ///         .into_iter()
653
    ///         .map(|e| NonZeroU32::new(e).unwrap())
654
    ///         .collect::<Vec<_>>()
655
    /// );
656
    /// ```
657
    pub fn iter_naive(&'a self, largest: NonZeroU32) -> impl Iterator<Item = NonZeroU32> + 'a {
34✔
658
        let ranges = simplify(self.clone(), largest, false);
34✔
659

660
        SequenceSetIter {
34✔
661
            ranges,
34✔
662
            active_range: None,
34✔
663
        }
34✔
664
    }
34✔
665
}
666

667
impl SeqOrUid {
668
    pub fn expand(&self, largest: NonZeroU32) -> NonZeroU32 {
284✔
669
        match self {
284✔
670
            SeqOrUid::Value(value) => *value,
224✔
671
            SeqOrUid::Asterisk => largest,
60✔
672
        }
673
    }
284✔
674
}
675

676
// -------------------------------------------------------------------------------------------------
677

678
struct SequenceSetIter {
679
    ranges: VecDeque<(u32, u32)>,
680
    active_range: Option<Sorting>,
681
}
682

683
impl Iterator for SequenceSetIter {
684
    type Item = NonZeroU32;
685

686
    fn next(&mut self) -> Option<Self::Item> {
722✔
687
        loop {
688
            match self.active_range {
926✔
689
                Some(ref mut range) => match range.next() {
768✔
690
                    // We know here that `next >= 1`.
691
                    Some(next) => break Some(NonZeroU32::new(next).unwrap()),
666✔
692
                    None => self.active_range = None,
102✔
693
                },
694
                None => match self.ranges.pop_front() {
158✔
695
                    Some((a, b)) => {
102✔
696
                        if a <= b {
102✔
697
                            self.active_range = Some(Sorting::Ascending(a..=b));
90✔
698
                        } else {
90✔
699
                            self.active_range = Some(Sorting::Descending((b..=a).rev()));
12✔
700
                        }
12✔
701
                    }
702
                    None => break None,
56✔
703
                },
704
            }
705
        }
706
    }
722✔
707
}
708

709
enum Sorting {
710
    Ascending(RangeInclusive<u32>),
711
    Descending(Rev<RangeInclusive<u32>>),
712
}
713

714
impl Iterator for Sorting {
715
    type Item = u32;
716

717
    fn next(&mut self) -> Option<Self::Item> {
768✔
718
        match self {
768✔
719
            Sorting::Ascending(iter) => iter.next(),
710✔
720
            Sorting::Descending(iter) => iter.next(),
58✔
721
        }
722
    }
768✔
723
}
724

725
// Simplify sequence set into VecDeque<(u32, u32)>:
726
// * Use u32 instead of NonZeroU32 (for internal purposes)
727
// * Expand Single(a) to (a, a)
728
// * Sort Range(a, b) so that a <= b
729
fn simplify(sequence_set: SequenceSet, largest: NonZeroU32, sort: bool) -> VecDeque<(u32, u32)> {
56✔
730
    sequence_set
56✔
731
        .0
56✔
732
        .0
56✔
733
        .into_iter()
56✔
734
        .map(|seq| match seq {
170✔
735
            Sequence::Single(a) => (u32::from(a.expand(largest)), u32::from(a.expand(largest))),
70✔
736
            Sequence::Range(a, b) => {
72✔
737
                let a = u32::from(a.expand(largest));
72✔
738
                let b = u32::from(b.expand(largest));
72✔
739

740
                if sort {
72✔
741
                    if a <= b { (a, b) } else { (b, a) }
30✔
742
                } else {
743
                    (a, b)
42✔
744
                }
745
            }
746
        })
142✔
747
        .collect()
56✔
748
}
56✔
749

750
fn cleanup(remaining: VecDeque<(u32, u32)>) -> VecDeque<(u32, u32)> {
22✔
751
    let mut remaining = {
22✔
752
        let mut tmp = Vec::from(remaining);
22✔
753
        tmp.sort();
22✔
754
        VecDeque::from(tmp)
22✔
755
    };
756

757
    let mut stack = VecDeque::new();
22✔
758
    stack.push_back(remaining.pop_front().unwrap());
22✔
759

760
    for (x, y) in remaining.into_iter() {
40✔
761
        let last = stack.back_mut().unwrap();
40✔
762

763
        if last.0 <= x && x <= last.1.saturating_add(1) {
40✔
764
            last.1 = max(last.1, y);
40✔
765
        } else {
40✔
UNCOV
766
            stack.push_back((x, y));
×
UNCOV
767
        }
×
768
    }
769

770
    stack
22✔
771
}
22✔
772

773
fn merge_ranges<T: Ord + Copy>(mut ranges: Vec<Range<T>>) -> Vec<Range<T>> {
243✔
774
    if ranges.is_empty() {
243✔
NEW
UNCOV
775
        return ranges;
×
776
    }
243✔
777

778
    ranges.sort_unstable_by_key(|r| r.start);
243✔
779

780
    let mut merged = Vec::with_capacity(ranges.len());
243✔
781
    merged.push(ranges[0].clone());
243✔
782

783
    for range in ranges.into_iter().skip(1) {
259✔
784
        let last = merged.last_mut().unwrap();
38✔
785
        if range.start < last.end {
38✔
786
            last.end = last.end.max(range.end);
28✔
787
        } else {
28✔
788
            merged.push(range);
10✔
789
        }
10✔
790
    }
791

792
    merged
243✔
793
}
243✔
794

795
#[cfg(test)]
796
mod tests {
797
    use std::num::NonZeroU32;
798

799
    use super::*;
800
    use crate::core::Vec1;
801

802
    #[test]
803
    fn test_creation_of_sequence_from_u32() {
2✔
804
        assert_eq!(
2✔
805
            SequenceSet::try_from(1),
2✔
806
            Ok(SequenceSet(Vec1::from(Sequence::Single(SeqOrUid::Value(
2✔
807
                NonZeroU32::new(1).unwrap()
2✔
808
            )))))
2✔
809
        );
810
        assert_eq!(
2✔
811
            SequenceSet::try_from(0),
2✔
812
            Err(ValidationError::new(ValidationErrorKind::Invalid))
2✔
813
        );
814
    }
2✔
815

816
    #[test]
817
    fn test_creation_of_sequence_from_range() {
2✔
818
        // 1:*
819
        let range = ..;
2✔
820
        let seq = Sequence::from(range);
2✔
821
        assert_eq!(
2✔
822
            seq,
823
            Sequence::Range(
2✔
824
                SeqOrUid::Value(NonZeroU32::new(1).unwrap()),
2✔
825
                SeqOrUid::Asterisk
2✔
826
            )
2✔
827
        );
828

829
        // 1:*
830
        let range = 1..;
2✔
831
        let seq = Sequence::try_from(range).unwrap();
2✔
832
        assert_eq!(
2✔
833
            seq,
834
            Sequence::Range(
2✔
835
                SeqOrUid::Value(NonZeroU32::new(1).unwrap()),
2✔
836
                SeqOrUid::Asterisk
2✔
837
            )
2✔
838
        );
839

840
        // 1337:*
841
        let range = 1337..;
2✔
842
        let seq = Sequence::try_from(range).unwrap();
2✔
843
        assert_eq!(
2✔
844
            seq,
845
            Sequence::Range(
2✔
846
                SeqOrUid::Value(NonZeroU32::new(1337).unwrap()),
2✔
847
                SeqOrUid::Asterisk
2✔
848
            )
2✔
849
        );
850

851
        // 1:1336
852
        let range = 1..1337;
2✔
853
        let seq = Sequence::try_from(range).unwrap();
2✔
854
        assert_eq!(
2✔
855
            seq,
856
            Sequence::Range(
2✔
857
                SeqOrUid::Value(NonZeroU32::new(1).unwrap()),
2✔
858
                SeqOrUid::Value(NonZeroU32::new(1336).unwrap())
2✔
859
            )
2✔
860
        );
861

862
        // 1:1337
863
        let range = 1..=1337;
2✔
864
        let seq = Sequence::try_from(range).unwrap();
2✔
865
        assert_eq!(
2✔
866
            seq,
867
            Sequence::Range(
2✔
868
                SeqOrUid::Value(NonZeroU32::new(1).unwrap()),
2✔
869
                SeqOrUid::Value(NonZeroU32::new(1337).unwrap())
2✔
870
            )
2✔
871
        );
872

873
        // 1:1336
874
        let range = ..1337;
2✔
875
        let seq = Sequence::try_from(range).unwrap();
2✔
876
        assert_eq!(
2✔
877
            seq,
878
            Sequence::Range(
2✔
879
                SeqOrUid::Value(NonZeroU32::new(1).unwrap()),
2✔
880
                SeqOrUid::Value(NonZeroU32::new(1336).unwrap())
2✔
881
            )
2✔
882
        );
883

884
        // 1:1337
885
        let range = ..=1337;
2✔
886
        let seq = Sequence::try_from(range).unwrap();
2✔
887
        assert_eq!(
2✔
888
            seq,
889
            Sequence::Range(
2✔
890
                SeqOrUid::Value(NonZeroU32::new(1).unwrap()),
2✔
891
                SeqOrUid::Value(NonZeroU32::new(1337).unwrap())
2✔
892
            )
2✔
893
        );
894
    }
2✔
895

896
    #[test]
897
    fn test_creation_of_sequence_set_from_str_positive() {
2✔
898
        let tests = &[
2✔
899
            (
2✔
900
                "1",
2✔
901
                SequenceSet(
2✔
902
                    vec![Sequence::Single(SeqOrUid::Value(1.try_into().unwrap()))]
2✔
903
                        .try_into()
2✔
904
                        .unwrap(),
2✔
905
                ),
2✔
906
            ),
2✔
907
            (
2✔
908
                "1,2,3",
2✔
909
                SequenceSet(
2✔
910
                    vec![
2✔
911
                        Sequence::Single(SeqOrUid::Value(1.try_into().unwrap())),
2✔
912
                        Sequence::Single(SeqOrUid::Value(2.try_into().unwrap())),
2✔
913
                        Sequence::Single(SeqOrUid::Value(3.try_into().unwrap())),
2✔
914
                    ]
2✔
915
                    .try_into()
2✔
916
                    .unwrap(),
2✔
917
                ),
2✔
918
            ),
2✔
919
            (
2✔
920
                "*",
2✔
921
                SequenceSet(
2✔
922
                    vec![Sequence::Single(SeqOrUid::Asterisk)]
2✔
923
                        .try_into()
2✔
924
                        .unwrap(),
2✔
925
                ),
2✔
926
            ),
2✔
927
            (
2✔
928
                "1:2",
2✔
929
                SequenceSet(
2✔
930
                    vec![Sequence::Range(
2✔
931
                        SeqOrUid::Value(1.try_into().unwrap()),
2✔
932
                        SeqOrUid::Value(2.try_into().unwrap()),
2✔
933
                    )]
2✔
934
                    .try_into()
2✔
935
                    .unwrap(),
2✔
936
                ),
2✔
937
            ),
2✔
938
            (
2✔
939
                "1:2,3",
2✔
940
                SequenceSet(
2✔
941
                    vec![
2✔
942
                        Sequence::Range(
2✔
943
                            SeqOrUid::Value(1.try_into().unwrap()),
2✔
944
                            SeqOrUid::Value(2.try_into().unwrap()),
2✔
945
                        ),
2✔
946
                        Sequence::Single(SeqOrUid::Value(3.try_into().unwrap())),
2✔
947
                    ]
2✔
948
                    .try_into()
2✔
949
                    .unwrap(),
2✔
950
                ),
2✔
951
            ),
2✔
952
            (
2✔
953
                "1:2,3,*",
2✔
954
                SequenceSet(
2✔
955
                    vec![
2✔
956
                        Sequence::Range(
2✔
957
                            SeqOrUid::Value(1.try_into().unwrap()),
2✔
958
                            SeqOrUid::Value(2.try_into().unwrap()),
2✔
959
                        ),
2✔
960
                        Sequence::Single(SeqOrUid::Value(3.try_into().unwrap())),
2✔
961
                        Sequence::Single(SeqOrUid::Asterisk),
2✔
962
                    ]
2✔
963
                    .try_into()
2✔
964
                    .unwrap(),
2✔
965
                ),
2✔
966
            ),
2✔
967
        ];
2✔
968

969
        for (test, expected) in tests.iter() {
12✔
970
            let got = SequenceSet::try_from(*test).unwrap();
12✔
971
            assert_eq!(*expected, got);
12✔
972
        }
973
    }
2✔
974

975
    #[test]
976
    fn test_creation_of_sequence_set_from_str_negative() {
2✔
977
        let tests = &[
2✔
978
            "", "* ", " *", " * ", "1 ", " 1", " 1 ", "01", " 01", "01 ", " 01 ", "*1", ":", ":*",
2✔
979
            "*:", "*: ", "1:2:3",
2✔
980
        ];
2✔
981

982
        for test in tests {
34✔
983
            let got = SequenceSet::try_from(*test);
34✔
984
            print!("\"{}\" | {:?} | ", test, got.clone().unwrap_err());
34✔
985
            println!("{}", got.unwrap_err());
34✔
986
        }
34✔
987
    }
2✔
988

989
    #[test]
990
    fn test_iteration_over_some_sequence_sets() {
2✔
991
        let tests = vec![
2✔
992
            ("*", vec![3]),
2✔
993
            ("1:*", vec![1, 2, 3]),
2✔
994
            ("5,1:*,2:*", vec![5, 1, 2, 3, 2, 3]),
2✔
995
            ("*:2", vec![3, 2]),
2✔
996
            ("*:*", vec![3]),
2✔
997
            ("4:6,*", vec![4, 5, 6, 3]),
2✔
998
        ]
999
        .into_iter()
2✔
1000
        .map(|(raw, vec)| {
13✔
1001
            (
1002
                raw,
12✔
1003
                vec.into_iter()
12✔
1004
                    .map(|num| num.try_into().unwrap())
34✔
1005
                    .collect::<Vec<NonZeroU32>>(),
12✔
1006
            )
1007
        })
12✔
1008
        .collect::<Vec<(&str, Vec<NonZeroU32>)>>();
2✔
1009

1010
        for (test, expected) in tests {
12✔
1011
            let seq_set = SequenceSet::try_from(test).unwrap();
12✔
1012
            let got: Vec<NonZeroU32> = seq_set.iter_naive(3.try_into().unwrap()).collect();
12✔
1013
            assert_eq!(*expected, got);
12✔
1014
        }
1015
    }
2✔
1016

1017
    /// See https://github.com/duesee/imap-codec/issues/411
1018
    #[test]
1019
    fn test_issue_411() {
2✔
1020
        let seq = SequenceSet::try_from("22,21,22,*:20").unwrap();
2✔
1021
        let largest = NonZeroU32::new(23).unwrap();
2✔
1022

1023
        // Naive
1024
        {
1025
            let expected = [22, 21, 22, 23, 22, 21, 20]
2✔
1026
                .map(|n| NonZeroU32::new(n).unwrap())
15✔
1027
                .to_vec();
2✔
1028
            let got: Vec<_> = seq.iter_naive(largest).collect();
2✔
1029

1030
            assert_eq!(expected, got);
2✔
1031
        }
1032

1033
        // Clean
1034
        {
1035
            let expected = [20, 21, 22, 23]
2✔
1036
                .map(|n| NonZeroU32::new(n).unwrap())
9✔
1037
                .to_vec();
2✔
1038
            let got: Vec<_> = seq.iter(largest).collect();
2✔
1039

1040
            assert_eq!(expected, got);
2✔
1041
        }
1042
    }
2✔
1043

1044
    #[test]
1045
    fn test_clean() {
2✔
1046
        let tests = vec![
2✔
1047
            "1",
1048
            "2",
2✔
1049
            "*",
2✔
1050
            "1:*",
2✔
1051
            "2:*",
2✔
1052
            "*:*",
2✔
1053
            "3,2,1",
2✔
1054
            "3,2,2,2,1,1,1",
2✔
1055
            "3:1,5:1,1:2,1:1",
2✔
1056
            "4:5,5:1,1:2,1:1,*:*,*:10,1:100",
2✔
1057
        ];
1058

1059
        for test in tests {
20✔
1060
            let seq = SequenceSet::try_from(test).unwrap();
20✔
1061
            let largest = NonZeroU32::new(13).unwrap();
20✔
1062

1063
            let naive = {
20✔
1064
                let mut naive: Vec<_> = seq.iter_naive(largest).collect();
20✔
1065
                naive.sort();
20✔
1066
                naive.dedup();
20✔
1067
                naive
20✔
1068
            };
1069
            let clean: Vec<_> = seq.iter(largest).collect();
20✔
1070

1071
            assert_eq!(naive, clean);
20✔
1072
        }
1073
    }
2✔
1074

1075
    #[test]
1076
    fn normalize_sequence() {
2✔
1077
        let tests = vec![
2✔
1078
            ("1:*", "1:*"),
2✔
1079
            ("*:1", "1:*"),
2✔
1080
            ("1", "1"),
2✔
1081
            ("1:1", "1"),
2✔
1082
            ("1:2", "1:2"),
2✔
1083
            ("2:1", "1:2"),
2✔
1084
        ];
1085

1086
        for (test, expected) in tests {
12✔
1087
            let expected = Sequence::try_from(expected).unwrap();
12✔
1088
            let mut got = Sequence::try_from(test).unwrap();
12✔
1089
            got.normalize();
12✔
1090
            assert_eq!(expected, got);
12✔
1091
        }
1092
    }
2✔
1093

1094
    #[test]
1095
    fn normalize_sequence_set() {
2✔
1096
        let tests = [
2✔
1097
            ("1,2,3,4", "1:4"),
2✔
1098
            ("4,1,3,2", "1:4"),
2✔
1099
            ("3,1,2,4", "1:4"),
2✔
1100
            ("3:1,5", "1:3,5"),
2✔
1101
            ("5,3:1", "1:3,5"),
2✔
1102
            ("3,3:1", "1:3"),
2✔
1103
            ("3:1,1", "1:3"),
2✔
1104
            ("3:1,2:5", "1:5"),
2✔
1105
            ("3:1,4:9", "1:3,4:9"),
2✔
1106
            ("9:4,3:1", "1:3,4:9"),
2✔
1107
            ("8:10,3:1,2:5,9", "1:5,8:10"),
2✔
1108
        ];
2✔
1109

1110
        for (test, expected) in tests {
22✔
1111
            let expected = SequenceSet::try_from(expected).unwrap();
22✔
1112
            let mut got = SequenceSet::try_from(test).unwrap();
22✔
1113
            got.normalize();
22✔
1114
            assert_eq!(expected, got);
22✔
1115
        }
1116
    }
2✔
1117
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc