opening_hours/utils/
range.rs1use std::cmp::{max, min};
2use std::ops::Range;
3use std::sync::Arc;
4
5use chrono::NaiveDateTime;
6use opening_hours_syntax::rules::RuleKind;
7
8#[non_exhaustive]
11#[derive(Clone, Debug, Eq, PartialEq)]
12pub struct DateTimeRange<D = NaiveDateTime> {
13 pub range: Range<D>,
14 pub kind: RuleKind,
15 pub comment: Arc<str>,
16}
17
18impl<D> DateTimeRange<D> {
19 pub fn into_state(self) -> (RuleKind, Arc<str>) {
22 (self.kind, self.comment)
23 }
24}
25
26pub(crate) fn ranges_union<T: Ord>(
29 ranges: impl IntoIterator<Item = Range<T>>,
30) -> impl Iterator<Item = Range<T>> {
31 let mut ranges: Vec<_> = ranges.into_iter().collect();
34 ranges.sort_unstable_by(|r1, r2| r1.start.cmp(&r2.start));
35
36 let mut ranges = ranges.into_iter().peekable();
38
39 std::iter::from_fn(move || {
40 let mut current = ranges.next()?;
41
42 while let Some(item) = ranges.next_if(|item| current.end >= item.start) {
43 if item.end > current.end {
45 current.end = item.end;
46 }
47 }
48
49 Some(current)
50 })
51}
52
53pub(crate) fn range_intersection<T: Ord>(range_1: Range<T>, range_2: Range<T>) -> Option<Range<T>> {
54 let result = max(range_1.start, range_2.start)..min(range_1.end, range_2.end);
55
56 if result.start < result.end {
57 Some(result)
58 } else {
59 None
60 }
61}
62
63#[cfg(test)]
64mod test {
65 use super::{range_intersection, ranges_union};
66
67 #[test]
68 fn test_unions() {
69 assert_eq!(
70 &ranges_union([1..5, 0..1, 3..7, 8..9]).collect::<Vec<_>>(),
71 &[0..7, 8..9]
72 );
73 }
74
75 #[test]
76 fn test_intersection() {
77 assert!(range_intersection(0..1, 1..2).is_none());
78 assert_eq!(range_intersection(0..3, 1..2).unwrap(), 1..2);
79 assert_eq!(range_intersection(0..3, 2..4).unwrap(), 2..3);
80 }
81}