1 // SPDX-License-Identifier: MPL-2.0
2 
3 //! Ranges are constraints defining sets of versions.
4 //!
5 //! Concretely, those constraints correspond to any set of versions
6 //! representable as the concatenation, union, and complement
7 //! of the ranges building blocks.
8 //!
9 //! Those building blocks are:
10 //!  - [none()](Range::none): the empty set
11 //!  - [any()](Range::any): the set of all possible versions
12 //!  - [exact(v)](Range::exact): the set containing only the version v
13 //!  - [higher_than(v)](Range::higher_than): the set defined by `v <= versions`
14 //!  - [strictly_lower_than(v)](Range::strictly_lower_than): the set defined by `versions < v`
15 //!  - [between(v1, v2)](Range::between): the set defined by `v1 <= versions < v2`
16 
17 use std::cmp::Ordering;
18 use std::fmt;
19 
20 use crate::internal::small_vec::SmallVec;
21 use crate::version::Version;
22 
23 /// A Range is a set of versions.
24 #[derive(Debug, Clone, Eq, PartialEq)]
25 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26 #[cfg_attr(feature = "serde", serde(transparent))]
27 pub struct Range<V: Version> {
28     segments: SmallVec<Interval<V>>,
29 }
30 
31 type Interval<V> = (V, Option<V>);
32 
33 // Range building blocks.
34 impl<V: Version> Range<V> {
35     /// Empty set of versions.
none() -> Self36     pub fn none() -> Self {
37         Self {
38             segments: SmallVec::empty(),
39         }
40     }
41 
42     /// Set of all possible versions.
any() -> Self43     pub fn any() -> Self {
44         Self::higher_than(V::lowest())
45     }
46 
47     /// Set containing exactly one version.
exact(v: impl Into<V>) -> Self48     pub fn exact(v: impl Into<V>) -> Self {
49         let v = v.into();
50         Self {
51             segments: SmallVec::one((v.clone(), Some(v.bump()))),
52         }
53     }
54 
55     /// Set of all versions higher or equal to some version.
higher_than(v: impl Into<V>) -> Self56     pub fn higher_than(v: impl Into<V>) -> Self {
57         Self {
58             segments: SmallVec::one((v.into(), None)),
59         }
60     }
61 
62     /// Set of all versions strictly lower than some version.
strictly_lower_than(v: impl Into<V>) -> Self63     pub fn strictly_lower_than(v: impl Into<V>) -> Self {
64         let v = v.into();
65         if v == V::lowest() {
66             Self::none()
67         } else {
68             Self {
69                 segments: SmallVec::one((V::lowest(), Some(v))),
70             }
71         }
72     }
73 
74     /// Set of all versions comprised between two given versions.
75     /// The lower bound is included and the higher bound excluded.
76     /// `v1 <= v < v2`.
between(v1: impl Into<V>, v2: impl Into<V>) -> Self77     pub fn between(v1: impl Into<V>, v2: impl Into<V>) -> Self {
78         let v1 = v1.into();
79         let v2 = v2.into();
80         if v1 < v2 {
81             Self {
82                 segments: SmallVec::one((v1, Some(v2))),
83             }
84         } else {
85             Self::none()
86         }
87     }
88 }
89 
90 // Set operations.
91 impl<V: Version> Range<V> {
92     // Negate ##################################################################
93 
94     /// Compute the complement set of versions.
negate(&self) -> Self95     pub fn negate(&self) -> Self {
96         match self.segments.first() {
97             None => Self::any(), // Complement of ∅  is *
98 
99             // First high bound is +∞
100             Some((v, None)) => {
101                 // Complement of * is ∅
102                 if v == &V::lowest() {
103                     Self::none()
104                 // Complement of "v <= _" is "_ < v"
105                 } else {
106                     Self::strictly_lower_than(v.clone())
107                 }
108             }
109 
110             // First high bound is not +∞
111             Some((v1, Some(v2))) => {
112                 if v1 == &V::lowest() {
113                     Self::negate_segments(v2.clone(), &self.segments[1..])
114                 } else {
115                     Self::negate_segments(V::lowest(), &self.segments)
116                 }
117             }
118         }
119     }
120 
121     /// Helper function performing the negation of intervals in segments.
122     /// For example:
123     ///    [ (v1, None) ] => [ (start, Some(v1)) ]
124     ///    [ (v1, Some(v2)) ] => [ (start, Some(v1)), (v2, None) ]
negate_segments(start: V, segments: &[Interval<V>]) -> Range<V>125     fn negate_segments(start: V, segments: &[Interval<V>]) -> Range<V> {
126         let mut complement_segments = SmallVec::empty();
127         let mut start = Some(start);
128         for (v1, maybe_v2) in segments {
129             // start.unwrap() is fine because `segments` is not exposed,
130             // and our usage guaranties that only the last segment may contain a None.
131             complement_segments.push((start.unwrap(), Some(v1.to_owned())));
132             start = maybe_v2.to_owned();
133         }
134         if let Some(last) = start {
135             complement_segments.push((last, None));
136         }
137 
138         Self {
139             segments: complement_segments,
140         }
141     }
142 
143     // Union and intersection ##################################################
144 
145     /// Compute the union of two sets of versions.
union(&self, other: &Self) -> Self146     pub fn union(&self, other: &Self) -> Self {
147         self.negate().intersection(&other.negate()).negate()
148     }
149 
150     /// Compute the intersection of two sets of versions.
intersection(&self, other: &Self) -> Self151     pub fn intersection(&self, other: &Self) -> Self {
152         let mut segments = SmallVec::empty();
153         let mut left_iter = self.segments.iter();
154         let mut right_iter = other.segments.iter();
155         let mut left = left_iter.next();
156         let mut right = right_iter.next();
157         loop {
158             match (left, right) {
159                 // Both left and right still contain a finite interval:
160                 (Some((l1, Some(l2))), Some((r1, Some(r2)))) => {
161                     if l2 <= r1 {
162                         // Intervals are disjoint, progress on the left.
163                         left = left_iter.next();
164                     } else if r2 <= l1 {
165                         // Intervals are disjoint, progress on the right.
166                         right = right_iter.next();
167                     } else {
168                         // Intervals are not disjoint.
169                         let start = l1.max(r1).to_owned();
170                         if l2 < r2 {
171                             segments.push((start, Some(l2.to_owned())));
172                             left = left_iter.next();
173                         } else {
174                             segments.push((start, Some(r2.to_owned())));
175                             right = right_iter.next();
176                         }
177                     }
178                 }
179 
180                 // Right contains an infinite interval:
181                 (Some((l1, Some(l2))), Some((r1, None))) => match l2.cmp(r1) {
182                     Ordering::Less => {
183                         left = left_iter.next();
184                     }
185                     Ordering::Equal => {
186                         for l in left_iter.cloned() {
187                             segments.push(l)
188                         }
189                         break;
190                     }
191                     Ordering::Greater => {
192                         let start = l1.max(r1).to_owned();
193                         segments.push((start, Some(l2.to_owned())));
194                         for l in left_iter.cloned() {
195                             segments.push(l)
196                         }
197                         break;
198                     }
199                 },
200 
201                 // Left contains an infinite interval:
202                 (Some((l1, None)), Some((r1, Some(r2)))) => match r2.cmp(l1) {
203                     Ordering::Less => {
204                         right = right_iter.next();
205                     }
206                     Ordering::Equal => {
207                         for r in right_iter.cloned() {
208                             segments.push(r)
209                         }
210                         break;
211                     }
212                     Ordering::Greater => {
213                         let start = l1.max(r1).to_owned();
214                         segments.push((start, Some(r2.to_owned())));
215                         for r in right_iter.cloned() {
216                             segments.push(r)
217                         }
218                         break;
219                     }
220                 },
221 
222                 // Both sides contain an infinite interval:
223                 (Some((l1, None)), Some((r1, None))) => {
224                     let start = l1.max(r1).to_owned();
225                     segments.push((start, None));
226                     break;
227                 }
228 
229                 // Left or right has ended.
230                 _ => {
231                     break;
232                 }
233             }
234         }
235 
236         Self { segments }
237     }
238 }
239 
240 // Other useful functions.
241 impl<V: Version> Range<V> {
242     /// Check if a range contains a given version.
contains(&self, version: &V) -> bool243     pub fn contains(&self, version: &V) -> bool {
244         for (v1, maybe_v2) in &self.segments {
245             match maybe_v2 {
246                 None => return v1 <= version,
247                 Some(v2) => {
248                     if version < v1 {
249                         return false;
250                     } else if version < v2 {
251                         return true;
252                     }
253                 }
254             }
255         }
256         false
257     }
258 
259     /// Return the lowest version in the range (if there is one).
lowest_version(&self) -> Option<V>260     pub fn lowest_version(&self) -> Option<V> {
261         self.segments.first().map(|(start, _)| start).cloned()
262     }
263 }
264 
265 // REPORT ######################################################################
266 
267 impl<V: Version> fmt::Display for Range<V> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result268     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269         match self.segments.as_slice() {
270             [] => write!(f, "∅"),
271             [(start, None)] if start == &V::lowest() => write!(f, "∗"),
272             [(start, None)] => write!(f, "{} <= v", start),
273             [(start, Some(end))] if end == &start.bump() => write!(f, "{}", start),
274             [(start, Some(end))] if start == &V::lowest() => write!(f, "v < {}", end),
275             [(start, Some(end))] => write!(f, "{} <= v < {}", start, end),
276             more_than_one_interval => {
277                 let string_intervals: Vec<_> = more_than_one_interval
278                     .iter()
279                     .map(interval_to_string)
280                     .collect();
281                 write!(f, "{}", string_intervals.join("  "))
282             }
283         }
284     }
285 }
286 
interval_to_string<V: Version>((start, maybe_end): &Interval<V>) -> String287 fn interval_to_string<V: Version>((start, maybe_end): &Interval<V>) -> String {
288     match maybe_end {
289         Some(end) => format!("[ {}, {} [", start, end),
290         None => format!("[ {}, ∞ [", start),
291     }
292 }
293 
294 // TESTS #######################################################################
295 
296 #[cfg(test)]
297 pub mod tests {
298     use proptest::prelude::*;
299 
300     use crate::version::NumberVersion;
301 
302     use super::*;
303 
strategy() -> impl Strategy<Value = Range<NumberVersion>>304     pub fn strategy() -> impl Strategy<Value = Range<NumberVersion>> {
305         prop::collection::vec(any::<u32>(), 0..10).prop_map(|mut vec| {
306             vec.sort_unstable();
307             vec.dedup();
308             let mut pair_iter = vec.chunks_exact(2);
309             let mut segments = SmallVec::empty();
310             while let Some([v1, v2]) = pair_iter.next() {
311                 segments.push((NumberVersion(*v1), Some(NumberVersion(*v2))));
312             }
313             if let [v] = pair_iter.remainder() {
314                 segments.push((NumberVersion(*v), None));
315             }
316             Range { segments }
317         })
318     }
319 
version_strat() -> impl Strategy<Value = NumberVersion>320     fn version_strat() -> impl Strategy<Value = NumberVersion> {
321         any::<u32>().prop_map(NumberVersion)
322     }
323 
324     proptest! {
325 
326         // Testing negate ----------------------------------
327 
328         #[test]
329         fn negate_is_different(range in strategy()) {
330             assert_ne!(range.negate(), range);
331         }
332 
333         #[test]
334         fn double_negate_is_identity(range in strategy()) {
335             assert_eq!(range.negate().negate(), range);
336         }
337 
338         #[test]
339         fn negate_contains_opposite(range in strategy(), version in version_strat()) {
340             assert_ne!(range.contains(&version), range.negate().contains(&version));
341         }
342 
343         // Testing intersection ----------------------------
344 
345         #[test]
346         fn intersection_is_symmetric(r1 in strategy(), r2 in strategy()) {
347             assert_eq!(r1.intersection(&r2), r2.intersection(&r1));
348         }
349 
350         #[test]
351         fn intersection_with_any_is_identity(range in strategy()) {
352             assert_eq!(Range::any().intersection(&range), range);
353         }
354 
355         #[test]
356         fn intersection_with_none_is_none(range in strategy()) {
357             assert_eq!(Range::none().intersection(&range), Range::none());
358         }
359 
360         #[test]
361         fn intersection_is_idempotent(r1 in strategy(), r2 in strategy()) {
362             assert_eq!(r1.intersection(&r2).intersection(&r2), r1.intersection(&r2));
363         }
364 
365         #[test]
366         fn intersection_is_associative(r1 in strategy(), r2 in strategy(), r3 in strategy()) {
367             assert_eq!(r1.intersection(&r2).intersection(&r3), r1.intersection(&r2.intersection(&r3)));
368         }
369 
370         #[test]
371         fn intesection_of_complements_is_none(range in strategy()) {
372             assert_eq!(range.negate().intersection(&range), Range::none());
373         }
374 
375         #[test]
376         fn intesection_contains_both(r1 in strategy(), r2 in strategy(), version in version_strat()) {
377             assert_eq!(r1.intersection(&r2).contains(&version), r1.contains(&version) && r2.contains(&version));
378         }
379 
380         // Testing union -----------------------------------
381 
382         #[test]
383         fn union_of_complements_is_any(range in strategy()) {
384             assert_eq!(range.negate().union(&range), Range::any());
385         }
386 
387         #[test]
388         fn union_contains_either(r1 in strategy(), r2 in strategy(), version in version_strat()) {
389             assert_eq!(r1.union(&r2).contains(&version), r1.contains(&version) || r2.contains(&version));
390         }
391 
392         // Testing contains --------------------------------
393 
394         #[test]
395         fn always_contains_exact(version in version_strat()) {
396             assert!(Range::exact(version).contains(&version));
397         }
398 
399         #[test]
400         fn contains_negation(range in strategy(), version in version_strat()) {
401             assert_ne!(range.contains(&version), range.negate().contains(&version));
402         }
403 
404         #[test]
405         fn contains_intersection(range in strategy(), version in version_strat()) {
406             assert_eq!(range.contains(&version), range.intersection(&Range::exact(version)) != Range::none());
407         }
408     }
409 }
410