opening_hours/
schedule.rs1use std::cmp::{max, min};
2use std::iter::Peekable;
3use std::mem::take;
4use std::ops::Range;
5use std::sync::Arc;
6
7use opening_hours_syntax::sorted_vec::UniqueSortedVec;
8use opening_hours_syntax::{ExtendedTime, RuleKind};
9
10#[derive(Clone, Debug, Eq, PartialEq)]
12pub struct TimeRange {
13 pub range: Range<ExtendedTime>,
15 pub kind: RuleKind,
17 pub comments: UniqueSortedVec<Arc<str>>,
19}
20
21impl TimeRange {
22 pub fn new(
24 range: Range<ExtendedTime>,
25 kind: RuleKind,
26 comments: UniqueSortedVec<Arc<str>>,
27 ) -> Self {
28 TimeRange { range, kind, comments }
29 }
30}
31
32#[derive(Clone, Debug, Default, Eq, PartialEq)]
38pub struct Schedule {
39 pub(crate) inner: Vec<TimeRange>,
41}
42
43impl Schedule {
44 pub fn new() -> Self {
52 Self::default()
53 }
54
55 pub fn from_ranges(
79 ranges: impl IntoIterator<Item = Range<ExtendedTime>>,
80 kind: RuleKind,
81 comments: &UniqueSortedVec<Arc<str>>,
82 ) -> Self {
83 let mut inner: Vec<_> = ranges
84 .into_iter()
85 .filter(|range| range.start < range.end)
86 .map(|range| TimeRange { range, kind, comments: comments.clone() })
87 .collect();
88
89 inner.sort_unstable_by_key(|rng| rng.range.start);
91 let mut i = 0;
92
93 while i + 1 < inner.len() {
94 if inner[i].range.end >= inner[i + 1].range.start {
95 inner[i].range.end = inner[i + 1].range.end;
96 let comments_left = std::mem::take(&mut inner[i].comments);
97 let comments_right = inner.remove(i + 1).comments;
98 inner[i].comments = comments_left.union(comments_right);
99 } else {
100 i += 1;
101 }
102 }
103
104 Self { inner }
105 }
106
107 pub fn is_empty(&self) -> bool {
115 self.inner.is_empty()
116 }
117
118 pub(crate) fn is_always_closed(&self) -> bool {
120 self.inner.iter().all(|rg| rg.kind == RuleKind::Closed)
121 }
122
123 pub fn addition(self, mut other: Self) -> Self {
125 match other.inner.pop() {
128 None => self,
129 Some(tr) => self.insert(tr).addition(other),
130 }
131 }
132
133 fn insert(self, mut ins_tr: TimeRange) -> Self {
135 let ins_start = ins_tr.range.start;
138 let ins_end = ins_tr.range.end;
139
140 let mut before: Vec<_> = self
141 .inner
142 .iter()
143 .filter(|tr| tr.range.start < ins_end)
144 .cloned()
145 .filter_map(|mut tr| {
146 tr.range.end = min(tr.range.end, ins_tr.range.start);
147
148 if tr.range.start < tr.range.end {
149 Some(tr)
150 } else {
151 ins_tr.comments = take(&mut ins_tr.comments).union(tr.comments);
152 None
153 }
154 })
155 .collect();
156
157 let mut after = self
158 .inner
159 .into_iter()
160 .filter(|tr| tr.range.end > ins_start)
161 .filter_map(|mut tr| {
162 tr.range.start = max(tr.range.start, ins_tr.range.end);
163
164 if tr.range.start < tr.range.end {
165 Some(tr)
166 } else {
167 ins_tr.comments = take(&mut ins_tr.comments).union(tr.comments);
168 None
169 }
170 })
171 .collect::<Vec<_>>()
172 .into_iter()
173 .peekable();
174
175 while before
178 .last()
179 .map(|tr| tr.range.end == ins_tr.range.start && tr.kind == ins_tr.kind)
180 .unwrap_or(false)
181 {
182 let tr = before.pop().unwrap();
183 ins_tr.range.start = tr.range.start;
184 ins_tr.comments = tr.comments.union(ins_tr.comments);
185 }
186
187 while after
188 .peek()
189 .map(|tr| ins_tr.range.end == tr.range.start && tr.kind == ins_tr.kind)
190 .unwrap_or(false)
191 {
192 let tr = after.next().unwrap();
193 ins_tr.range.end = tr.range.end;
194 ins_tr.comments = tr.comments.union(ins_tr.comments);
195 }
196
197 let mut inner = before;
200 inner.push(ins_tr);
201 inner.extend(after);
202 Schedule { inner }
203 }
204}
205
206impl IntoIterator for Schedule {
207 type Item = TimeRange;
208 type IntoIter = IntoIter;
209
210 fn into_iter(self) -> Self::IntoIter {
211 IntoIter::new(self)
212 }
213}
214
215#[derive(Debug)]
217pub struct IntoIter {
218 last_end: ExtendedTime,
219 ranges: Peekable<std::vec::IntoIter<TimeRange>>,
220}
221
222impl IntoIter {
223 const HOLES_STATE: RuleKind = RuleKind::Closed;
225
226 fn new(schedule: Schedule) -> Self {
228 Self {
229 last_end: ExtendedTime::MIDNIGHT_00,
230 ranges: schedule.inner.into_iter().peekable(),
231 }
232 }
233
234 fn pre_yield(&mut self, value: TimeRange) -> Option<TimeRange> {
236 assert!(
237 value.range.start < value.range.end,
238 "infinite loop detected"
239 );
240
241 self.last_end = value.range.end;
242 Some(value)
243 }
244}
245
246impl Iterator for IntoIter {
247 type Item = TimeRange;
248
249 fn next(&mut self) -> Option<Self::Item> {
250 if self.last_end >= ExtendedTime::MIDNIGHT_24 {
251 return None;
253 }
254
255 let mut yielded_range = {
256 let next_start = self.ranges.peek().map(|tr| tr.range.start);
257
258 if next_start == Some(self.last_end) {
259 self.ranges.next().unwrap()
261 } else {
262 TimeRange::new(
264 self.last_end..next_start.unwrap_or(self.last_end),
265 Self::HOLES_STATE,
266 UniqueSortedVec::new(),
267 )
268 }
269 };
270
271 while let Some(next_range) = self.ranges.peek() {
272 if next_range.range.start > yielded_range.range.end {
273 if yielded_range.kind == Self::HOLES_STATE {
274 yielded_range.range.end = next_range.range.start;
276 } else {
277 return self.pre_yield(yielded_range);
279 }
280 }
281
282 if yielded_range.kind != next_range.kind {
283 return self.pre_yield(yielded_range);
285 }
286
287 let next_range = self.ranges.next().unwrap();
288 yielded_range.range.end = next_range.range.end;
289 yielded_range.comments = yielded_range.comments.union(next_range.comments);
290 }
291
292 if yielded_range.kind == Self::HOLES_STATE {
293 yielded_range.range.end = ExtendedTime::MIDNIGHT_24;
295 }
296
297 self.pre_yield(yielded_range)
298 }
299}
300
301impl std::iter::FusedIterator for IntoIter {}
302
303#[macro_export]
327macro_rules! schedule {
328 (
329 $( $hh1:expr,$mm1:expr $( => $kind:expr $( , $comment:expr )* => $hh2:expr,$mm2:expr )+ );*
330 $( ; )?
331 ) => {{
332 #[allow(unused_imports)]
333 use $crate::schedule::{Schedule, TimeRange};
334
335 #[allow(unused_imports)]
336 use opening_hours_syntax::extended_time::ExtendedTime;
337
338 #[allow(unused_mut)]
339 let mut schedule = Schedule::new();
340
341 $(
342 let mut prev = ExtendedTime::new($hh1, $mm1)
343 .expect("Invalid interval start");
344
345 $(
346 let curr = ExtendedTime::new($hh2, $mm2)
347 .expect("Invalid interval end");
348
349 let comments = vec![$(std::sync::Arc::from($comment)),*].into();
350 let next_schedule = Schedule::from_ranges([prev..curr], $kind, &comments);
351 schedule = schedule.addition(next_schedule);
352
353 #[allow(unused_assignments)]
354 { prev = curr }
355 )+
356 )*
357
358 schedule
359 }};
360}