opening_hours/utils/
range.rs1use std::cmp::{max, min};
2use std::ops::{Range, RangeInclusive};
3use std::sync::Arc;
4
5use chrono::NaiveDateTime;
6use opening_hours_syntax::rules::RuleKind;
7use opening_hours_syntax::sorted_vec::UniqueSortedVec;
8
9#[non_exhaustive]
12#[derive(Clone, Debug, Eq, PartialEq)]
13pub struct DateTimeRange<D = NaiveDateTime> {
14 pub range: Range<D>,
15 pub kind: RuleKind,
16 pub comments: UniqueSortedVec<Arc<str>>,
17}
18
19impl<D> DateTimeRange<D> {
20 pub(crate) fn new_with_sorted_comments(
21 range: Range<D>,
22 kind: RuleKind,
23 comments: UniqueSortedVec<Arc<str>>,
24 ) -> Self {
25 Self { range, kind, comments }
26 }
27}
28
29pub(crate) trait WrappingRange<T> {
32 fn wrapping_contains(&self, elt: &T) -> bool;
33}
34
35impl<T: PartialOrd> WrappingRange<T> for RangeInclusive<T> {
36 fn wrapping_contains(&self, elt: &T) -> bool {
37 if self.start() <= self.end() {
38 self.contains(elt)
39 } else {
40 self.start() <= elt || elt <= self.end()
41 }
42 }
43}
44
45pub(crate) fn ranges_union<T: Ord>(
48 ranges: impl IntoIterator<Item = Range<T>>,
49) -> impl Iterator<Item = Range<T>> {
50 let mut ranges: Vec<_> = ranges.into_iter().collect();
53 ranges.sort_unstable_by(|r1, r2| r1.start.cmp(&r2.start));
54
55 let mut ranges = ranges.into_iter();
57 let mut current_opt = ranges.next();
58
59 std::iter::from_fn(move || {
60 if let Some(ref mut current) = current_opt {
61 #[allow(clippy::while_let_on_iterator)]
62 while let Some(item) = ranges.next() {
63 if current.end >= item.start {
64 if item.end > current.end {
66 current.end = item.end;
67 }
68 } else {
69 return Some(current_opt.replace(item).unwrap());
70 }
71 }
72
73 Some(current_opt.take().unwrap())
74 } else {
75 None
76 }
77 })
78}
79
80pub(crate) fn range_intersection<T: Ord>(range_1: Range<T>, range_2: Range<T>) -> Option<Range<T>> {
81 let result = max(range_1.start, range_2.start)..min(range_1.end, range_2.end);
82
83 if result.start < result.end {
84 Some(result)
85 } else {
86 None
87 }
88}
89
90#[cfg(test)]
91mod test {
92 use super::{range_intersection, ranges_union};
93
94 #[test]
95 fn test_unions() {
96 assert_eq!(
97 &ranges_union([1..5, 0..1, 3..7, 8..9]).collect::<Vec<_>>(),
98 &[0..7, 8..9]
99 );
100 }
101
102 #[test]
103 fn test_intersection() {
104 assert!(range_intersection(0..1, 1..2).is_none());
105 assert_eq!(range_intersection(0..3, 1..2).unwrap(), 1..2);
106 assert_eq!(range_intersection(0..3, 2..4).unwrap(), 2..3);
107 }
108}