Skip to main content

periodical/iter/intervals/
complement.rs

1//! Complement iterator
2//!
3//! Iterator that returns the [`ComplementResult`] of every interval from the
4//! interval.
5//!
6//! The iterator uses [`Complementable`] to get the complements of the
7//! intervals.
8//!
9//! # Examples
10//!
11//! ```
12//! # use std::error::Error;
13//! # use jiff::Zoned;
14//! # use periodical::ops::ComplementResult;
15//! # use periodical::intervals::absolute::{
16//! #     AbsBoundPair, AbsEndBound, AbsFiniteBoundPos, AbsStartBound,
17//! # };
18//! # use periodical::intervals::meta::BoundInclusivity;
19//! # use periodical::iter::intervals::complement::ComplementIteratorDispatcher;
20//! let intervals = [
21//!     AbsBoundPair::new(
22//!         AbsFiniteBoundPos::new(
23//!             "2025-01-01 08:00:00[Europe/Oslo]"
24//!                 .parse::<Zoned>()?
25//!                 .timestamp(),
26//!         )
27//!         .to_start_bound(),
28//!         AbsFiniteBoundPos::new(
29//!             "2025-01-01 11:00:00[Europe/Oslo]"
30//!                 .parse::<Zoned>()?
31//!                 .timestamp(),
32//!         )
33//!         .to_end_bound(),
34//!     ),
35//!     AbsBoundPair::new(
36//!         AbsFiniteBoundPos::new(
37//!             "2025-01-01 12:00:00[Europe/Oslo]"
38//!                 .parse::<Zoned>()?
39//!                 .timestamp(),
40//!         )
41//!         .to_start_bound(),
42//!         AbsFiniteBoundPos::new(
43//!             "2025-01-01 16:00:00[Europe/Oslo]"
44//!                 .parse::<Zoned>()?
45//!                 .timestamp(),
46//!         )
47//!         .to_end_bound(),
48//!     ),
49//! ];
50//!
51//! assert_eq!(
52//!     intervals.complement().collect::<Vec<_>>(),
53//!     vec![
54//!         ComplementResult::Split(
55//!             AbsBoundPair::new(
56//!                 AbsStartBound::InfinitePast,
57//!                 AbsFiniteBoundPos::new_with_incl(
58//!                     "2025-01-01 08:00:00[Europe/Oslo]"
59//!                         .parse::<Zoned>()?
60//!                         .timestamp(),
61//!                     BoundInclusivity::Exclusive,
62//!                 )
63//!                 .to_end_bound(),
64//!             )
65//!             .to_emptiable(),
66//!             AbsBoundPair::new(
67//!                 AbsFiniteBoundPos::new_with_incl(
68//!                     "2025-01-01 11:00:00[Europe/Oslo]"
69//!                         .parse::<Zoned>()?
70//!                         .timestamp(),
71//!                     BoundInclusivity::Exclusive,
72//!                 )
73//!                 .to_start_bound(),
74//!                 AbsEndBound::InfiniteFuture,
75//!             )
76//!             .to_emptiable(),
77//!         ),
78//!         ComplementResult::Split(
79//!             AbsBoundPair::new(
80//!                 AbsStartBound::InfinitePast,
81//!                 AbsFiniteBoundPos::new_with_incl(
82//!                     "2025-01-01 12:00:00[Europe/Oslo]"
83//!                         .parse::<Zoned>()?
84//!                         .timestamp(),
85//!                     BoundInclusivity::Exclusive,
86//!                 )
87//!                 .to_end_bound(),
88//!             )
89//!             .to_emptiable(),
90//!             AbsBoundPair::new(
91//!                 AbsFiniteBoundPos::new_with_incl(
92//!                     "2025-01-01 16:00:00[Europe/Oslo]"
93//!                         .parse::<Zoned>()?
94//!                         .timestamp(),
95//!                     BoundInclusivity::Exclusive,
96//!                 )
97//!                 .to_start_bound(),
98//!                 AbsEndBound::InfiniteFuture,
99//!             )
100//!             .to_emptiable(),
101//!         ),
102//!     ],
103//! );
104//! # Ok::<(), Box<dyn Error>>(())
105//! ```
106
107use crate::intervals::ops::Complementable;
108use crate::ops::ComplementResult;
109
110/// Dispatcher trait for the [`ComplementIter`] iterator
111pub trait ComplementIteratorDispatcher
112where
113    Self: IntoIterator + Sized,
114    Self::Item: Complementable,
115{
116    /// Creates a [`ComplementIter`] from the collection
117    fn complement(self) -> ComplementIter<Self::IntoIter> {
118        ComplementIter::new(self.into_iter())
119    }
120}
121
122impl<I> ComplementIteratorDispatcher for I
123where
124    I: IntoIterator + Sized,
125    I::Item: Complementable,
126{
127}
128
129/// Returns the interval complement of each element
130#[derive(Debug, Clone, Hash)]
131pub struct ComplementIter<I> {
132    iter: I,
133}
134
135impl<I> ComplementIter<I>
136where
137    I: Iterator,
138    I::Item: Complementable,
139{
140    pub fn new(iter: I) -> Self {
141        ComplementIter {
142            iter,
143        }
144    }
145}
146
147impl<I> Iterator for ComplementIter<I>
148where
149    I: Iterator,
150    I::Item: Complementable,
151{
152    type Item = ComplementResult<<I::Item as Complementable>::Output>;
153
154    fn next(&mut self) -> Option<Self::Item> {
155        Some(self.iter.next()?.complement())
156    }
157
158    fn size_hint(&self) -> (usize, Option<usize>) {
159        self.iter.size_hint()
160    }
161}
162
163impl<I> DoubleEndedIterator for ComplementIter<I>
164where
165    I: DoubleEndedIterator,
166    I::Item: Complementable,
167{
168    fn next_back(&mut self) -> Option<Self::Item> {
169        Some(self.iter.next_back()?.complement())
170    }
171}