Skip to main content

opening_hours/utils/
range.rs

1use std::cmp::{max, min};
2use std::ops::Range;
3use std::sync::Arc;
4
5use chrono::NaiveDateTime;
6use opening_hours_syntax::rules::RuleKind;
7
8// DateTimeRange
9
10#[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    /// Extract the kind and comment from the range, which are the values that define current state
20    /// of an expression.
21    pub fn into_state(self) -> (RuleKind, Arc<str>) {
22        (self.kind, self.comment)
23    }
24}
25
26// Range operations
27
28pub(crate) fn ranges_union<T: Ord>(
29    ranges: impl IntoIterator<Item = Range<T>>,
30) -> impl Iterator<Item = Range<T>> {
31    // TODO (optimisation): we could gain performance by ensuring that range iterators are always
32    // sorted.
33    let mut ranges: Vec<_> = ranges.into_iter().collect();
34    ranges.sort_unstable_by(|r1, r2| r1.start.cmp(&r2.start));
35
36    // Get ranges by increasing start
37    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            // The two intervals intersect with each other
44            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}