Coverage Report

Created: 2025-07-11 06:43

/rust/registry/src/index.crates.io-6f17d22bba15001f/wyz-0.5.1/src/range.rs
Line
Count
Source (jump to first uncovered line)
1
//! Range utilities.
2
3
use core::ops::{
4
  Bound,
5
  Range,
6
  RangeBounds,
7
};
8
9
/// Extension methods for working with various range types.
10
pub trait RangeExt<T>: RangeBounds<T>
11
where T: Ord
12
{
13
  /// Normalizes a range-like type to a canonical half-open `Range`.
14
  ///
15
  /// ## Parameters
16
  ///
17
  /// - `self`: The range to normalize.
18
  /// - `start`: An optional fallback *inclusive* lower bound.
19
  /// - `end`: An optional fallback *exclusive* upper bound.
20
  ///
21
  /// ## Returns
22
  ///
23
  /// A `Range` whose start and end values are the following, in order of
24
  /// decreasing priority:
25
  ///
26
  /// - `self.start()`, or if absent, the `start` parameter, or if it is
27
  ///   `None`, `0`.
28
  /// - `self.end()`, or if absent, the `end` parameter, or if it is `None`,
29
  ///   !0`.
30
  fn normalize(
31
    self,
32
    start: impl Into<Option<T>>,
33
    end: impl Into<Option<T>>,
34
  ) -> Range<T>;
35
36
  /// Finds the intersection between two range-likes. The produced `Range`
37
  /// spans only the elements common to both.
38
  ///
39
  /// This returns `None` if the ranges do not have an intersection (at least
40
  /// one element present in both ranges).
41
  fn intersection<R>(self, other: R) -> Option<Range<T>>
42
  where R: RangeExt<T>;
43
44
  /// Finds the union between two range-likes. The produced `Range` spans all
45
  /// elements present in at least one of them.
46
  ///
47
  /// This returns `None` if the ranges do not have an intersection (at least
48
  /// one element present in both ranges).
49
  fn union<R>(self, other: R) -> Option<Range<T>>
50
  where R: RangeExt<T>;
51
}
52
53
//  TODO(myrrlyn): Use funty to extend this for all integers.
54
impl<R> RangeExt<usize> for R
55
where R: RangeBounds<usize>
56
{
57
29.8k
  fn normalize(
58
29.8k
    self,
59
29.8k
    start: impl Into<Option<usize>>,
60
29.8k
    end: impl Into<Option<usize>>,
61
29.8k
  ) -> Range<usize> {
62
29.8k
    let start = match self.start_bound() {
63
0
      Bound::Unbounded => start.into().unwrap_or(0),
64
29.8k
      Bound::Included(&v) => v,
65
0
      Bound::Excluded(&v) => v.saturating_add(1),
66
    };
67
29.8k
    let end = match self.end_bound() {
68
0
      Bound::Unbounded => end.into().unwrap_or(!0),
69
0
      Bound::Included(&v) => v.saturating_add(1),
70
29.8k
      Bound::Excluded(&v) => v,
71
    };
72
29.8k
    if start > end {
73
0
      end .. start
74
    }
75
    else {
76
29.8k
      start .. end
77
    }
78
29.8k
  }
79
80
  fn intersection<R2>(self, other: R2) -> Option<Range<usize>>
81
  where R2: RangeExt<usize> {
82
    let Range { start: a1, end: a2 } = self.normalize(None, None);
83
    let Range { start: b1, end: b2 } = other.normalize(None, None);
84
    if b1 < a1 {
85
      return (b1 .. b2).intersection(a1 .. a2);
86
    }
87
    if !(a1 .. a2).contains(&b1) {
88
      return None;
89
    }
90
    let start = a1.max(b1);
91
    let end = a2.min(b2);
92
    if start > end {
93
      Some(end .. start)
94
    }
95
    else {
96
      Some(start .. end)
97
    }
98
  }
99
100
  fn union<R2>(self, other: R2) -> Option<Range<usize>>
101
  where R2: RangeExt<usize> {
102
    let Range { start: a1, end: a2 } = self.normalize(None, None);
103
    let Range { start: b1, end: b2 } = other.normalize(None, None);
104
    if b1 < a1 {
105
      return (b1 .. b2).intersection(a1 .. a2);
106
    }
107
    if !(a1 .. a2).contains(&b1) {
108
      return None;
109
    }
110
    let start = a1.min(b1);
111
    let end = a2.max(b2);
112
    if start > end {
113
      Some(end .. start)
114
    }
115
    else {
116
      Some(start .. end)
117
    }
118
  }
119
}
120
121
#[cfg(test)]
122
mod tests {
123
  use super::*;
124
125
  #[test]
126
  fn normalize() {
127
    let r = (..).normalize(1, 10);
128
    assert!(r.contains(&1));
129
    assert!(r.contains(&9));
130
    assert!(!r.contains(&0));
131
    assert!(!r.contains(&10));
132
133
    let r = (.. 10).normalize(1, 20);
134
    assert!(r.contains(&1));
135
    assert!(r.contains(&9));
136
    assert!(!r.contains(&0));
137
    assert!(!r.contains(&10));
138
139
    let r = (4 ..).normalize(6, 10);
140
    assert!(r.contains(&4));
141
    assert!(r.contains(&9));
142
    assert!(!r.contains(&3));
143
    assert!(!r.contains(&10));
144
145
    let r = (4 ..= 10).normalize(6, 8);
146
    assert!(r.contains(&4));
147
    assert!(r.contains(&10));
148
    assert!(!r.contains(&3));
149
    assert!(!r.contains(&11));
150
151
    let r = (..= 10).normalize(1, 8);
152
    assert!(r.contains(&1));
153
    assert!(r.contains(&10));
154
    assert!(!r.contains(&0));
155
    assert!(!r.contains(&11));
156
  }
157
158
  #[test]
159
  fn intersect() {
160
    let a = 3 .. 10;
161
    let b = 7 ..= 20;
162
    assert_eq!(a.intersection(b), Some(7 .. 10));
163
164
    let c = 3 .. 10;
165
    let d = 13 ..= 20;
166
    assert!(c.intersection(d).is_none());
167
  }
168
169
  #[test]
170
  fn union() {
171
    let a = 3 .. 10;
172
    let b = 7 ..= 20;
173
    assert_eq!(a.union(b), Some(3 .. 21));
174
175
    let c = 3 .. 10;
176
    let d = 13 ..= 20;
177
    assert!(c.union(d).is_none());
178
  }
179
}