1#[cfg(feature = "semver")]
39pub mod semver;
40
41use std::borrow::Borrow;
42use std::cmp::Ordering;
43use std::fmt::{Debug, Display, Formatter};
44use std::ops::Bound::{self, Excluded, Included, Unbounded};
45use std::ops::RangeBounds;
46
47#[cfg(any(feature = "proptest", test))]
48use proptest::prelude::*;
49use smallvec::{smallvec, SmallVec};
50
51#[derive(Debug, Clone, Eq, PartialEq, Hash)]
72#[cfg_attr(feature = "serde", derive(serde::Serialize))]
73#[cfg_attr(feature = "serde", serde(transparent))]
74pub struct Ranges<V> {
75 segments: SmallVec<[Interval<V>; 1]>,
79}
80
81type Interval<V> = (Bound<V>, Bound<V>);
83
84impl<V> Ranges<V> {
85 pub fn empty() -> Self {
87 Self {
88 segments: SmallVec::new(),
89 }
90 }
91
92 pub fn full() -> Self {
94 Self {
95 segments: smallvec![(Unbounded, Unbounded)],
96 }
97 }
98
99 pub fn higher_than(v: impl Into<V>) -> Self {
101 Self {
102 segments: smallvec![(Included(v.into()), Unbounded)],
103 }
104 }
105
106 pub fn strictly_higher_than(v: impl Into<V>) -> Self {
108 Self {
109 segments: smallvec![(Excluded(v.into()), Unbounded)],
110 }
111 }
112
113 pub fn strictly_lower_than(v: impl Into<V>) -> Self {
115 Self {
116 segments: smallvec![(Unbounded, Excluded(v.into()))],
117 }
118 }
119
120 pub fn lower_than(v: impl Into<V>) -> Self {
122 Self {
123 segments: smallvec![(Unbounded, Included(v.into()))],
124 }
125 }
126
127 pub fn between(v1: impl Into<V>, v2: impl Into<V>) -> Self {
129 Self {
130 segments: smallvec![(Included(v1.into()), Excluded(v2.into()))],
131 }
132 }
133
134 pub fn is_empty(&self) -> bool {
136 self.segments.is_empty()
137 }
138}
139
140impl<V: Clone> Ranges<V> {
141 pub fn singleton(v: impl Into<V>) -> Self {
143 let v = v.into();
144 Self {
145 segments: smallvec![(Included(v.clone()), Included(v))],
146 }
147 }
148
149 pub fn complement(&self) -> Self {
151 match self.segments.first() {
152 None => Self::full(),
154
155 Some((Unbounded, Unbounded)) => Self::empty(),
157
158 Some((Included(v), Unbounded)) => Self::strictly_lower_than(v.clone()),
160 Some((Excluded(v), Unbounded)) => Self::lower_than(v.clone()),
161
162 Some((Unbounded, Included(v))) => {
163 Self::negate_segments(Excluded(v.clone()), &self.segments[1..])
164 }
165 Some((Unbounded, Excluded(v))) => {
166 Self::negate_segments(Included(v.clone()), &self.segments[1..])
167 }
168 Some((Included(_), Included(_)))
169 | Some((Included(_), Excluded(_)))
170 | Some((Excluded(_), Included(_)))
171 | Some((Excluded(_), Excluded(_))) => Self::negate_segments(Unbounded, &self.segments),
172 }
173 }
174
175 fn negate_segments(start: Bound<V>, segments: &[Interval<V>]) -> Self {
177 let mut complement_segments = SmallVec::new();
178 let mut start = start;
179 for (v1, v2) in segments {
180 complement_segments.push((
181 start,
182 match v1 {
183 Included(v) => Excluded(v.clone()),
184 Excluded(v) => Included(v.clone()),
185 Unbounded => unreachable!(),
186 },
187 ));
188 start = match v2 {
189 Included(v) => Excluded(v.clone()),
190 Excluded(v) => Included(v.clone()),
191 Unbounded => Unbounded,
192 }
193 }
194 if !matches!(start, Unbounded) {
195 complement_segments.push((start, Unbounded));
196 }
197
198 Self {
199 segments: complement_segments,
200 }
201 }
202}
203
204impl<V: Ord> Ranges<V> {
205 pub fn as_singleton(&self) -> Option<&V> {
207 match self.segments.as_slice() {
208 [(Included(v1), Included(v2))] => {
209 if v1 == v2 {
210 Some(v1)
211 } else {
212 None
213 }
214 }
215 _ => None,
216 }
217 }
218
219 pub fn bounding_range(&self) -> Option<(Bound<&V>, Bound<&V>)> {
225 self.segments.first().map(|(start, _)| {
226 let end = self
227 .segments
228 .last()
229 .expect("if there is a first element, there must be a last element");
230 (start.as_ref(), end.1.as_ref())
231 })
232 }
233
234 pub fn contains<Q>(&self, version: &Q) -> bool
236 where
237 V: Borrow<Q>,
238 Q: ?Sized + PartialOrd,
239 {
240 self.segments
241 .binary_search_by(|segment| {
242 within_bounds(version, segment).reverse()
245 })
246 .is_ok()
248 }
249
250 pub fn contains_many<'s, I, BV>(&'s self, versions: I) -> impl Iterator<Item = bool> + 's
256 where
257 I: Iterator<Item = BV> + 's,
258 BV: Borrow<V> + 's,
259 {
260 #[cfg(debug_assertions)]
261 let mut last: Option<BV> = None;
262 versions.scan(0, move |i, v| {
263 #[cfg(debug_assertions)]
264 {
265 if let Some(l) = last.as_ref() {
266 assert!(
267 l.borrow() <= v.borrow(),
268 "`contains_many` `versions` argument incorrectly sorted"
269 );
270 }
271 }
272 while let Some(segment) = self.segments.get(*i) {
273 match within_bounds(v.borrow(), segment) {
274 Ordering::Less => return Some(false),
275 Ordering::Equal => return Some(true),
276 Ordering::Greater => *i += 1,
277 }
278 }
279 #[cfg(debug_assertions)]
280 {
281 last = Some(v);
282 }
283 Some(false)
284 })
285 }
286
287 pub fn from_range_bounds<R, IV>(bounds: R) -> Self
289 where
290 R: RangeBounds<IV>,
291 IV: Clone + Into<V>,
292 {
293 let start = match bounds.start_bound() {
294 Included(v) => Included(v.clone().into()),
295 Excluded(v) => Excluded(v.clone().into()),
296 Unbounded => Unbounded,
297 };
298 let end = match bounds.end_bound() {
299 Included(v) => Included(v.clone().into()),
300 Excluded(v) => Excluded(v.clone().into()),
301 Unbounded => Unbounded,
302 };
303 if valid_segment(&start, &end) {
304 Self {
305 segments: smallvec![(start, end)],
306 }
307 } else {
308 Self::empty()
309 }
310 }
311
312 fn check_invariants(self) -> Self {
314 if cfg!(debug_assertions) {
315 for p in self.segments.as_slice().windows(2) {
316 assert!(end_before_start_with_gap(&p[0].1, &p[1].0));
317 }
318 for (s, e) in self.segments.iter() {
319 assert!(valid_segment(s, e));
320 }
321 }
322 self
323 }
324}
325
326fn cmp_bounds_start<V: PartialOrd>(left: Bound<&V>, right: Bound<&V>) -> Option<Ordering> {
340 Some(match (left, right) {
341 (Unbounded, Unbounded) => Ordering::Equal,
344 (Included(_left), Unbounded) => Ordering::Greater,
347 (Excluded(_left), Unbounded) => Ordering::Greater,
350 (Unbounded, Included(_right)) => Ordering::Less,
353 (Included(left), Included(right)) => left.partial_cmp(right)?,
356 (Excluded(left), Included(right)) => match left.partial_cmp(right)? {
357 Ordering::Less => Ordering::Less,
360 Ordering::Equal => Ordering::Greater,
363 Ordering::Greater => Ordering::Greater,
366 },
367 (Unbounded, Excluded(_right)) => Ordering::Less,
370 (Included(left), Excluded(right)) => match left.partial_cmp(right)? {
371 Ordering::Less => Ordering::Less,
374 Ordering::Equal => Ordering::Less,
377 Ordering::Greater => Ordering::Greater,
380 },
381 (Excluded(left), Excluded(right)) => left.partial_cmp(right)?,
384 })
385}
386
387fn cmp_bounds_end<V: PartialOrd>(left: Bound<&V>, right: Bound<&V>) -> Option<Ordering> {
403 Some(match (left, right) {
404 (Unbounded, Unbounded) => Ordering::Equal,
407 (Included(_left), Unbounded) => Ordering::Less,
410 (Excluded(_left), Unbounded) => Ordering::Less,
413 (Unbounded, Included(_right)) => Ordering::Greater,
416 (Included(left), Included(right)) => left.partial_cmp(right)?,
419 (Excluded(left), Included(right)) => match left.partial_cmp(right)? {
420 Ordering::Less => Ordering::Less,
423 Ordering::Equal => Ordering::Less,
426 Ordering::Greater => Ordering::Greater,
429 },
430 (Unbounded, Excluded(_right)) => Ordering::Greater,
431 (Included(left), Excluded(right)) => match left.partial_cmp(right)? {
432 Ordering::Less => Ordering::Less,
435 Ordering::Equal => Ordering::Greater,
438 Ordering::Greater => Ordering::Greater,
441 },
442 (Excluded(left), Excluded(right)) => left.partial_cmp(right)?,
445 })
446}
447
448impl<V: PartialOrd> PartialOrd for Ranges<V> {
449 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
453 for (left, right) in self.segments.iter().zip(other.segments.iter()) {
454 let start_cmp = cmp_bounds_start(left.start_bound(), right.start_bound())?;
455 if start_cmp != Ordering::Equal {
456 return Some(start_cmp);
457 }
458 let end_cmp = cmp_bounds_end(left.end_bound(), right.end_bound())?;
459 if end_cmp != Ordering::Equal {
460 return Some(end_cmp);
461 }
462 }
463 Some(self.segments.len().cmp(&other.segments.len()))
464 }
465}
466
467impl<V: Ord> Ord for Ranges<V> {
468 fn cmp(&self, other: &Self) -> Ordering {
469 self.partial_cmp(other)
470 .expect("PartialOrd must be `Some(Ordering)` for types that implement `Ord`")
471 }
472}
473
474fn within_bounds<Q, V>(version: &Q, segment: &Interval<V>) -> Ordering
481where
482 V: Borrow<Q>,
483 Q: ?Sized + PartialOrd,
484{
485 let below_lower_bound = match segment {
486 (Excluded(start), _) => version <= start.borrow(),
487 (Included(start), _) => version < start.borrow(),
488 (Unbounded, _) => false,
489 };
490 if below_lower_bound {
491 return Ordering::Less;
492 }
493 let below_upper_bound = match segment {
494 (_, Unbounded) => true,
495 (_, Included(end)) => version <= end.borrow(),
496 (_, Excluded(end)) => version < end.borrow(),
497 };
498 if below_upper_bound {
499 return Ordering::Equal;
500 }
501 Ordering::Greater
502}
503
504fn valid_segment<T: PartialOrd>(start: &Bound<T>, end: &Bound<T>) -> bool {
506 match (start, end) {
507 (Included(s), Included(e)) => s <= e,
509 (Included(s), Excluded(e)) => s < e,
510 (Excluded(s), Included(e)) => s < e,
511 (Excluded(s), Excluded(e)) => s < e,
512 (Unbounded, _) | (_, Unbounded) => true,
513 }
514}
515
516fn end_before_start_with_gap<V: PartialOrd>(end: &Bound<V>, start: &Bound<V>) -> bool {
534 match (end, start) {
535 (_, Unbounded) => false,
536 (Unbounded, _) => false,
537 (Included(left), Included(right)) => left < right,
538 (Included(left), Excluded(right)) => left < right,
539 (Excluded(left), Included(right)) => left < right,
540 (Excluded(left), Excluded(right)) => left <= right,
541 }
542}
543
544fn left_start_is_smaller<V: PartialOrd>(left: Bound<V>, right: Bound<V>) -> bool {
545 match (left, right) {
546 (Unbounded, _) => true,
547 (_, Unbounded) => false,
548 (Included(l), Included(r)) => l <= r,
549 (Excluded(l), Excluded(r)) => l <= r,
550 (Included(l), Excluded(r)) => l <= r,
551 (Excluded(l), Included(r)) => l < r,
552 }
553}
554
555fn left_end_is_smaller<V: PartialOrd>(left: Bound<V>, right: Bound<V>) -> bool {
556 match (left, right) {
557 (_, Unbounded) => true,
558 (Unbounded, _) => false,
559 (Included(l), Included(r)) => l <= r,
560 (Excluded(l), Excluded(r)) => l <= r,
561 (Excluded(l), Included(r)) => l <= r,
562 (Included(l), Excluded(r)) => l < r,
563 }
564}
565
566fn group_adjacent_locations(
575 mut locations: impl Iterator<Item = Option<usize>>,
576) -> impl Iterator<Item = (Option<usize>, Option<usize>)> {
577 let mut seg = locations.next().flatten().map(|ver| (None, Some(ver)));
579 std::iter::from_fn(move || {
580 for ver in locations.by_ref() {
581 if let Some(ver) = ver {
582 seg = Some((seg.map_or(Some(ver), |(s, _)| s), Some(ver)));
584 } else {
585 if seg.is_some() {
587 return seg.take();
588 }
589 }
590 }
591 seg.take().map(|(s, _)| (s, None))
593 })
594}
595
596impl<V: Ord + Clone> Ranges<V> {
597 pub fn union(&self, other: &Self) -> Self {
599 let mut output = SmallVec::new();
600 let mut accumulator: Option<(&Bound<_>, &Bound<_>)> = None;
601 let mut left_iter = self.segments.iter().peekable();
602 let mut right_iter = other.segments.iter().peekable();
603 loop {
604 let smaller_interval = match (left_iter.peek(), right_iter.peek()) {
605 (Some((left_start, left_end)), Some((right_start, right_end))) => {
606 if left_start_is_smaller(left_start.as_ref(), right_start.as_ref()) {
607 left_iter.next();
608 (left_start, left_end)
609 } else {
610 right_iter.next();
611 (right_start, right_end)
612 }
613 }
614 (Some((left_start, left_end)), None) => {
615 left_iter.next();
616 (left_start, left_end)
617 }
618 (None, Some((right_start, right_end))) => {
619 right_iter.next();
620 (right_start, right_end)
621 }
622 (None, None) => break,
623 };
624
625 if let Some(accumulator_) = accumulator {
626 if end_before_start_with_gap(accumulator_.1, smaller_interval.0) {
627 output.push((accumulator_.0.clone(), accumulator_.1.clone()));
628 accumulator = Some(smaller_interval);
629 } else {
630 let accumulator_end = match (accumulator_.1, smaller_interval.1) {
631 (_, Unbounded) | (Unbounded, _) => &Unbounded,
632 (Included(l), Excluded(r) | Included(r)) if l == r => accumulator_.1,
633 (Included(l) | Excluded(l), Included(r) | Excluded(r)) => {
634 if l > r {
635 accumulator_.1
636 } else {
637 smaller_interval.1
638 }
639 }
640 };
641 accumulator = Some((accumulator_.0, accumulator_end));
642 }
643 } else {
644 accumulator = Some(smaller_interval)
645 }
646 }
647
648 if let Some(accumulator) = accumulator {
649 output.push((accumulator.0.clone(), accumulator.1.clone()));
650 }
651
652 Self { segments: output }.check_invariants()
653 }
654
655 pub fn intersection(&self, other: &Self) -> Self {
657 let mut output = SmallVec::new();
658 let mut left_iter = self.segments.iter().peekable();
659 let mut right_iter = other.segments.iter().peekable();
660 while let Some(((left_start, left_end), (right_start, right_end))) =
667 left_iter.peek().zip(right_iter.peek())
668 {
669 let left_end_is_smaller = left_end_is_smaller(left_end.as_ref(), right_end.as_ref());
671 let (other_start, end) = if left_end_is_smaller {
677 left_iter.next();
678 (right_start, left_end)
679 } else {
680 right_iter.next();
681 (left_start, right_end)
682 };
683 if !valid_segment(other_start, end) {
689 continue;
692 }
693 let start = match (left_start, right_start) {
694 (Included(l), Included(r)) => Included(std::cmp::max(l, r)),
695 (Excluded(l), Excluded(r)) => Excluded(std::cmp::max(l, r)),
696
697 (Included(i), Excluded(e)) | (Excluded(e), Included(i)) => {
698 if i <= e {
699 Excluded(e)
700 } else {
701 Included(i)
702 }
703 }
704 (s, Unbounded) | (Unbounded, s) => s.as_ref(),
705 };
706 output.push((start.cloned(), end.clone()))
709 }
710
711 Self { segments: output }.check_invariants()
712 }
713
714 pub fn is_disjoint(&self, other: &Self) -> bool {
719 let mut left_iter = self.segments.iter().peekable();
721 let mut right_iter = other.segments.iter().peekable();
722
723 while let Some((left, right)) = left_iter.peek().zip(right_iter.peek()) {
724 if !valid_segment(&right.start_bound(), &left.end_bound()) {
725 left_iter.next();
726 } else if !valid_segment(&left.start_bound(), &right.end_bound()) {
727 right_iter.next();
728 } else {
729 return false;
730 }
731 }
732
733 true
735 }
736
737 pub fn subset_of(&self, other: &Self) -> bool {
742 let mut containing_iter = other.segments.iter();
743 let mut subset_iter = self.segments.iter();
744 let Some(mut containing_elem) = containing_iter.next() else {
745 return subset_iter.next().is_none();
747 };
748
749 for subset_elem in subset_iter {
750 while !valid_segment(&subset_elem.start_bound(), &containing_elem.end_bound()) {
753 if let Some(containing_elem_) = containing_iter.next() {
754 containing_elem = containing_elem_;
755 } else {
756 return false;
757 };
758 }
759
760 let start_contained =
761 left_start_is_smaller(containing_elem.start_bound(), subset_elem.start_bound());
762
763 if !start_contained {
764 return false;
766 }
767
768 let end_contained =
769 left_end_is_smaller(subset_elem.end_bound(), containing_elem.end_bound());
770
771 if !end_contained {
772 return false;
774 }
775 }
776
777 true
778 }
779
780 pub fn simplify<'s, I, BV>(&self, versions: I) -> Self
791 where
792 I: Iterator<Item = BV> + 's,
793 BV: Borrow<V> + 's,
794 {
795 if self.as_singleton().is_some() {
797 return self.clone();
798 }
799
800 #[cfg(debug_assertions)]
801 let mut last: Option<BV> = None;
802 let version_locations = versions.scan(0, move |i, v| {
804 #[cfg(debug_assertions)]
805 {
806 if let Some(l) = last.as_ref() {
807 assert!(
808 l.borrow() <= v.borrow(),
809 "`simplify` `versions` argument incorrectly sorted"
810 );
811 }
812 }
813 while let Some(segment) = self.segments.get(*i) {
814 match within_bounds(v.borrow(), segment) {
815 Ordering::Less => return Some(None),
816 Ordering::Equal => return Some(Some(*i)),
817 Ordering::Greater => *i += 1,
818 }
819 }
820 #[cfg(debug_assertions)]
821 {
822 last = Some(v);
823 }
824 Some(None)
825 });
826 let mut kept_segments = group_adjacent_locations(version_locations).peekable();
827
828 if kept_segments.peek().is_none() {
830 return self.clone();
831 }
832
833 self.keep_segments(kept_segments)
834 }
835
836 fn keep_segments(
841 &self,
842 kept_segments: impl Iterator<Item = (Option<usize>, Option<usize>)>,
843 ) -> Ranges<V> {
844 let mut segments = SmallVec::new();
845 for (s, e) in kept_segments {
846 segments.push((
847 s.map_or(Unbounded, |s| self.segments[s].0.clone()),
848 e.map_or(Unbounded, |e| self.segments[e].1.clone()),
849 ));
850 }
851 Self { segments }.check_invariants()
852 }
853
854 pub fn iter(&self) -> impl Iterator<Item = (&Bound<V>, &Bound<V>)> {
856 self.segments.iter().map(|(start, end)| (start, end))
857 }
858}
859
860pub struct RangesIter<V>(smallvec::IntoIter<[Interval<V>; 1]>);
862
863impl<V> Iterator for RangesIter<V> {
864 type Item = Interval<V>;
865
866 fn next(&mut self) -> Option<Self::Item> {
867 self.0.next()
868 }
869
870 fn size_hint(&self) -> (usize, Option<usize>) {
871 (self.0.len(), Some(self.0.len()))
872 }
873}
874
875impl<V> ExactSizeIterator for RangesIter<V> {}
876
877impl<V> DoubleEndedIterator for RangesIter<V> {
878 fn next_back(&mut self) -> Option<Self::Item> {
879 self.0.next_back()
880 }
881}
882
883impl<V> IntoIterator for Ranges<V> {
884 type Item = (Bound<V>, Bound<V>);
885 type IntoIter = RangesIter<V>;
887
888 fn into_iter(self) -> Self::IntoIter {
889 RangesIter(self.segments.into_iter())
890 }
891}
892
893impl<V: Ord> FromIterator<(Bound<V>, Bound<V>)> for Ranges<V> {
894 fn from_iter<T: IntoIterator<Item = (Bound<V>, Bound<V>)>>(iter: T) -> Self {
899 let mut segments: SmallVec<[Interval<V>; 1]> = SmallVec::new();
916
917 for segment in iter {
918 if !valid_segment(&segment.start_bound(), &segment.end_bound()) {
919 continue;
920 }
921 let insertion_point = segments.partition_point(|elem: &Interval<V>| {
923 cmp_bounds_start(elem.start_bound(), segment.start_bound())
924 .unwrap()
925 .is_lt()
926 });
927 let previous_overlapping = insertion_point > 0
929 && !end_before_start_with_gap(
930 &segments[insertion_point - 1].end_bound(),
931 &segment.start_bound(),
932 );
933
934 let next_overlapping = insertion_point < segments.len()
937 && !end_before_start_with_gap(
938 &segment.end_bound(),
939 &segments[insertion_point].start_bound(),
940 );
941
942 match (previous_overlapping, next_overlapping) {
943 (true, true) => {
944 let mut following = segments.remove(insertion_point);
967 while insertion_point < segments.len()
968 && !end_before_start_with_gap(
969 &segment.end_bound(),
970 &segments[insertion_point].start_bound(),
971 )
972 {
973 following = segments.remove(insertion_point);
974 }
975
976 if cmp_bounds_end(segment.end_bound(), following.end_bound())
978 .unwrap()
979 .is_lt()
980 {
981 segments[insertion_point - 1].1 = following.1;
982 } else {
983 segments[insertion_point - 1].1 = segment.1;
984 }
985 }
986 (true, false) => {
987 if cmp_bounds_end(
1002 segments[insertion_point - 1].end_bound(),
1003 segment.end_bound(),
1004 )
1005 .unwrap()
1006 .is_lt()
1007 {
1008 segments[insertion_point - 1].1 = segment.1;
1009 }
1010 }
1011 (false, true) => {
1012 while insertion_point + 1 < segments.len()
1036 && !end_before_start_with_gap(
1037 &segment.end_bound(),
1038 &segments[insertion_point + 1].start_bound(),
1039 )
1040 {
1041 segments.remove(insertion_point);
1044 }
1045
1046 if cmp_bounds_end(segments[insertion_point].end_bound(), segment.end_bound())
1048 .unwrap()
1049 .is_lt()
1050 {
1051 segments[insertion_point].1 = segment.1;
1052 }
1053 segments[insertion_point].0 = segment.0;
1054 }
1055 (false, false) => {
1056 segments.insert(insertion_point, segment);
1065 }
1066 }
1067 }
1068
1069 Self { segments }.check_invariants()
1070 }
1071}
1072
1073impl<V: Display + Eq> Display for Ranges<V> {
1076 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1077 if self.segments.is_empty() {
1078 write!(f, "∅")?;
1079 } else {
1080 for (idx, segment) in self.segments.iter().enumerate() {
1081 if idx > 0 {
1082 write!(f, " | ")?;
1083 }
1084 match segment {
1085 (Unbounded, Unbounded) => write!(f, "*")?,
1086 (Unbounded, Included(v)) => write!(f, "<={v}")?,
1087 (Unbounded, Excluded(v)) => write!(f, "<{v}")?,
1088 (Included(v), Unbounded) => write!(f, ">={v}")?,
1089 (Included(v), Included(b)) => {
1090 if v == b {
1091 write!(f, "{v}")?
1092 } else {
1093 write!(f, ">={v}, <={b}")?
1094 }
1095 }
1096 (Included(v), Excluded(b)) => write!(f, ">={v}, <{b}")?,
1097 (Excluded(v), Unbounded) => write!(f, ">{v}")?,
1098 (Excluded(v), Included(b)) => write!(f, ">{v}, <={b}")?,
1099 (Excluded(v), Excluded(b)) => write!(f, ">{v}, <{b}")?,
1100 };
1101 }
1102 }
1103 Ok(())
1104 }
1105}
1106
1107#[cfg(feature = "serde")]
1110impl<'de, V: serde::Deserialize<'de>> serde::Deserialize<'de> for Ranges<V> {
1111 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1112 #[derive(serde::Deserialize)]
1117 #[serde(untagged)]
1118 enum EitherInterval<V> {
1119 B(Bound<V>, Bound<V>),
1120 D(V, Option<V>),
1121 }
1122
1123 let bounds: SmallVec<[EitherInterval<V>; 2]> =
1124 serde::Deserialize::deserialize(deserializer)?;
1125
1126 let mut segments = SmallVec::new();
1127 for i in bounds {
1128 match i {
1129 EitherInterval::B(l, r) => segments.push((l, r)),
1130 EitherInterval::D(l, Some(r)) => segments.push((Included(l), Excluded(r))),
1131 EitherInterval::D(l, None) => segments.push((Included(l), Unbounded)),
1132 }
1133 }
1134
1135 Ok(Ranges { segments })
1136 }
1137}
1138
1139#[cfg(any(feature = "proptest", test))]
1142pub fn proptest_strategy() -> impl Strategy<Value = Ranges<u32>> {
1143 (
1144 any::<bool>(),
1145 prop::collection::vec(any::<(u32, bool)>(), 0..10),
1146 )
1147 .prop_map(|(start_unbounded, deltas)| {
1148 let mut start = if start_unbounded {
1149 Some(Unbounded)
1150 } else {
1151 None
1152 };
1153 let mut largest: u32 = 0;
1154 let mut last_bound_was_inclusive = false;
1155 let mut segments = SmallVec::new();
1156 for (delta, inclusive) in deltas {
1157 largest = match largest.checked_add(delta) {
1159 Some(s) => s,
1160 None => {
1161 continue;
1163 }
1164 };
1165
1166 let current_bound = if inclusive {
1167 Included(largest)
1168 } else {
1169 Excluded(largest)
1170 };
1171
1172 if let Some(start_bound) = start.take() {
1175 if delta == 0 && !(matches!(start_bound, Included(_)) && inclusive) {
1178 start = Some(start_bound);
1179 continue;
1180 }
1181 last_bound_was_inclusive = inclusive;
1182 segments.push((start_bound, current_bound));
1183 } else {
1184 if delta == 0 && (last_bound_was_inclusive || inclusive) {
1188 continue;
1189 }
1190 start = Some(current_bound);
1191 }
1192 }
1193
1194 if let Some(start_bound) = start {
1197 segments.push((start_bound, Unbounded));
1198 }
1199
1200 Ranges { segments }.check_invariants()
1201 })
1202}
1203
1204#[cfg(test)]
1205pub mod tests {
1206 use proptest::prelude::*;
1207
1208 use super::*;
1209
1210 fn version_strat() -> impl Strategy<Value = u32> {
1211 any::<u32>()
1212 }
1213
1214 proptest! {
1215
1216 #[cfg(feature = "serde")]
1219 #[test]
1220 fn serde_round_trip(range in proptest_strategy()) {
1221 let s = ron::ser::to_string(&range).unwrap();
1222 let r = ron::de::from_str(&s).unwrap();
1223 assert_eq!(range, r);
1224 }
1225
1226 #[test]
1229 fn negate_is_different(range in proptest_strategy()) {
1230 assert_ne!(range.complement(), range);
1231 }
1232
1233 #[test]
1234 fn double_negate_is_identity(range in proptest_strategy()) {
1235 assert_eq!(range.complement().complement(), range);
1236 }
1237
1238 #[test]
1239 fn negate_contains_opposite(range in proptest_strategy(), version in version_strat()) {
1240 assert_ne!(range.contains(&version), range.complement().contains(&version));
1241 }
1242
1243 #[test]
1246 fn intersection_is_symmetric(r1 in proptest_strategy(), r2 in proptest_strategy()) {
1247 assert_eq!(r1.intersection(&r2), r2.intersection(&r1));
1248 }
1249
1250 #[test]
1251 fn intersection_with_any_is_identity(range in proptest_strategy()) {
1252 assert_eq!(Ranges::full().intersection(&range), range);
1253 }
1254
1255 #[test]
1256 fn intersection_with_none_is_none(range in proptest_strategy()) {
1257 assert_eq!(Ranges::empty().intersection(&range), Ranges::empty());
1258 }
1259
1260 #[test]
1261 fn intersection_is_idempotent(r1 in proptest_strategy(), r2 in proptest_strategy()) {
1262 assert_eq!(r1.intersection(&r2).intersection(&r2), r1.intersection(&r2));
1263 }
1264
1265 #[test]
1266 fn intersection_is_associative(r1 in proptest_strategy(), r2 in proptest_strategy(), r3 in proptest_strategy()) {
1267 assert_eq!(r1.intersection(&r2).intersection(&r3), r1.intersection(&r2.intersection(&r3)));
1268 }
1269
1270 #[test]
1271 fn intesection_of_complements_is_none(range in proptest_strategy()) {
1272 assert_eq!(range.complement().intersection(&range), Ranges::empty());
1273 }
1274
1275 #[test]
1276 fn intesection_contains_both(r1 in proptest_strategy(), r2 in proptest_strategy(), version in version_strat()) {
1277 assert_eq!(r1.intersection(&r2).contains(&version), r1.contains(&version) && r2.contains(&version));
1278 }
1279
1280 #[test]
1283 fn union_of_complements_is_any(range in proptest_strategy()) {
1284 assert_eq!(range.complement().union(&range), Ranges::full());
1285 }
1286
1287 #[test]
1288 fn union_contains_either(r1 in proptest_strategy(), r2 in proptest_strategy(), version in version_strat()) {
1289 assert_eq!(r1.union(&r2).contains(&version), r1.contains(&version) || r2.contains(&version));
1290 }
1291
1292 #[test]
1293 fn is_disjoint_through_intersection(r1 in proptest_strategy(), r2 in proptest_strategy()) {
1294 let disjoint_def = r1.intersection(&r2) == Ranges::empty();
1295 assert_eq!(r1.is_disjoint(&r2), disjoint_def);
1296 }
1297
1298 #[test]
1299 fn subset_of_through_intersection(r1 in proptest_strategy(), r2 in proptest_strategy()) {
1300 let disjoint_def = r1.intersection(&r2) == r1;
1301 assert_eq!(r1.subset_of(&r2), disjoint_def);
1302 }
1303
1304 #[test]
1305 fn union_through_intersection(r1 in proptest_strategy(), r2 in proptest_strategy()) {
1306 let union_def = r1
1307 .complement()
1308 .intersection(&r2.complement())
1309 .complement()
1310 .check_invariants();
1311 assert_eq!(r1.union(&r2), union_def);
1312 }
1313
1314 #[test]
1317 fn always_contains_exact(version in version_strat()) {
1318 assert!(Ranges::<u32>::singleton(version).contains(&version));
1319 }
1320
1321 #[test]
1322 fn contains_negation(range in proptest_strategy(), version in version_strat()) {
1323 assert_ne!(range.contains(&version), range.complement().contains(&version));
1324 }
1325
1326 #[test]
1327 fn contains_intersection(range in proptest_strategy(), version in version_strat()) {
1328 assert_eq!(range.contains(&version), range.intersection(&Ranges::singleton(version)) != Ranges::empty());
1329 }
1330
1331 #[test]
1332 fn contains_bounding_range(range in proptest_strategy(), version in version_strat()) {
1333 if range.contains(&version) {
1334 assert!(range.bounding_range().map(|b| b.contains(&version)).unwrap_or(false));
1335 }
1336 }
1337
1338 #[test]
1339 fn from_range_bounds(range in any::<(Bound<u32>, Bound<u32>)>(), version in version_strat()) {
1340 let rv: Ranges<_> = Ranges::<u32>::from_range_bounds(range);
1341 assert_eq!(range.contains(&version), rv.contains(&version));
1342 }
1343
1344 #[test]
1345 fn from_range_bounds_round_trip(range in any::<(Bound<u32>, Bound<u32>)>()) {
1346 let rv: Ranges<u32> = Ranges::from_range_bounds(range);
1347 let rv2: Ranges<u32> = rv.bounding_range().map(Ranges::from_range_bounds::<_, u32>).unwrap_or_else(Ranges::empty);
1348 assert_eq!(rv, rv2);
1349 }
1350
1351 #[test]
1352 fn contains(range in proptest_strategy(), versions in proptest::collection::vec(version_strat(), ..30)) {
1353 for v in versions {
1354 assert_eq!(range.contains(&v), range.segments.iter().any(|s| RangeBounds::contains(s, &v)));
1355 }
1356 }
1357
1358 #[test]
1359 fn contains_many(range in proptest_strategy(), mut versions in proptest::collection::vec(version_strat(), ..30)) {
1360 versions.sort();
1361 assert_eq!(versions.len(), range.contains_many(versions.iter()).count());
1362 for (a, b) in versions.iter().zip(range.contains_many(versions.iter())) {
1363 assert_eq!(range.contains(a), b);
1364 }
1365 }
1366
1367 #[test]
1368 fn simplify(range in proptest_strategy(), mut versions in proptest::collection::vec(version_strat(), ..30)) {
1369 versions.sort();
1370 let simp = range.simplify(versions.iter());
1371
1372 for v in versions {
1373 assert_eq!(range.contains(&v), simp.contains(&v));
1374 }
1375 assert!(simp.segments.len() <= range.segments.len())
1376 }
1377
1378 #[test]
1379 fn from_iter_valid(segments in proptest::collection::vec(any::<(Bound<u32>, Bound<u32>)>(), ..30)) {
1380 let mut expected = Ranges::empty();
1381 for segment in &segments {
1382 expected = expected.union(&Ranges::from_range_bounds(*segment));
1383 }
1384 let actual = Ranges::from_iter(segments.clone());
1385 assert_eq!(expected, actual, "{segments:?}");
1386 }
1387 }
1388
1389 #[test]
1390 fn contains_many_can_take_owned() {
1391 let range: Ranges<u8> = Ranges::singleton(1);
1392 let versions = vec![1, 2, 3];
1393 assert_eq!(
1395 range.contains_many(versions.iter()).count(),
1396 range
1397 .contains_many(versions.iter().map(std::borrow::Cow::Borrowed))
1398 .count()
1399 );
1400 assert_eq!(
1402 range.contains_many(versions.iter()).count(),
1403 range.contains_many(versions.into_iter()).count()
1404 );
1405 }
1406
1407 #[test]
1408 fn contains_can_take_owned() {
1409 let range: Ranges<Box<u8>> = Ranges::singleton(1);
1410 let version = 1;
1411
1412 assert_eq!(range.contains(&Box::new(version)), range.contains(&version));
1413 let range: Ranges<String> = Ranges::singleton(1.to_string());
1414 let version = 1.to_string();
1415 assert_eq!(range.contains(&version), range.contains("1"));
1416 }
1417
1418 #[test]
1419 fn simplify_can_take_owned() {
1420 let range: Ranges<u8> = Ranges::singleton(1);
1421 let versions = vec![1, 2, 3];
1422 assert_eq!(
1424 range.simplify(versions.iter()),
1425 range.simplify(versions.iter().map(std::borrow::Cow::Borrowed))
1426 );
1427 assert_eq!(
1429 range.simplify(versions.iter()),
1430 range.simplify(versions.into_iter())
1431 );
1432 }
1433
1434 #[test]
1435 fn version_ord() {
1436 let versions: &[Ranges<u32>] = &[
1437 Ranges::strictly_lower_than(1u32),
1438 Ranges::lower_than(1u32),
1439 Ranges::singleton(1u32),
1440 Ranges::between(1u32, 3u32),
1441 Ranges::higher_than(1u32),
1442 Ranges::strictly_higher_than(1u32),
1443 Ranges::singleton(2u32),
1444 Ranges::singleton(2u32).union(&Ranges::singleton(3u32)),
1445 Ranges::singleton(2u32)
1446 .union(&Ranges::singleton(3u32))
1447 .union(&Ranges::singleton(4u32)),
1448 Ranges::singleton(2u32).union(&Ranges::singleton(4u32)),
1449 Ranges::singleton(3u32),
1450 ];
1451
1452 let mut versions_sorted = versions.to_vec();
1453 versions_sorted.sort();
1454 assert_eq!(versions_sorted, versions);
1455
1456 let mut version_reverse_sorted = versions.to_vec();
1458 version_reverse_sorted.reverse();
1459 version_reverse_sorted.sort();
1460 assert_eq!(version_reverse_sorted, versions);
1461 }
1462}