use alloc::boxed::Box;
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::ops::Range;
use crate::normalize::paving::{EmptyPavingSelector, Paving, Paving1D};
use crate::rules::time::{Time, TimeSelector, TimeSpan};
use crate::{ExtendedTime, RuleKind};
pub(crate) type TimeRules = Vec<((RuleKind, Arc<str>), TimeSelector)>;
pub(crate) fn no_overlap_with_next_day(selector: &TimeSelector) -> bool {
(selector.spans)
.iter()
.all(|span| match (span.range.start, span.range.end) {
(Time::Fixed(start), Time::Fixed(end)) => {
start < end && end <= ExtendedTime::MIDNIGHT_24
}
(Time::Fixed(start), Time::Variable(_)) => start == ExtendedTime::MIDNIGHT_00,
(Time::Variable(_), Time::Fixed(end)) => end == ExtendedTime::MIDNIGHT_24,
(Time::Variable(start), Time::Variable(end)) => start.is_before(&end),
})
}
pub(crate) fn normalize_time_rules(slot: TimeRules) -> TimeRules {
let mut result = Vec::new();
let mut canonical = TimeSelectorPaving::default();
let mut spans = (slot.into_iter())
.flat_map(|(state, selector)| {
selector
.spans
.into_iter()
.map(move |span| (state.clone(), span))
})
.peekable();
while let Some((state, span)) = spans.next() {
if (result.is_empty() || state != Default::default())
&& let Some(range) = time_span_to_daily_ranges(&span)
{
canonical.set_time_range(range, state);
continue;
}
let mut non_canonical = TimeSelector { spans: vec![span] };
while let Some((_, extra_span)) = spans.next_if(|(extra_state, _)| *extra_state == state) {
if (result.is_empty() || state != Default::default())
&& let Some(range) = time_span_to_daily_ranges(&extra_span)
{
canonical.set_time_range(range, state.clone());
} else {
non_canonical.spans.push(extra_span)
}
}
result.extend(canonical.into_time_selector());
canonical = TimeSelectorPaving::default();
non_canonical.spans.sort();
result.push((state, non_canonical));
}
result.extend(canonical.into_time_selector());
result
}
fn time_span_to_daily_ranges(span: &TimeSpan) -> Option<Range<ExtendedTime>> {
if span.open_end || span.repeats.is_some() {
return None;
}
match (span.range.start, span.range.end) {
(Time::Fixed(start), Time::Fixed(end))
if start < end && end <= ExtendedTime::MIDNIGHT_24 =>
{
Some(start..end)
}
_ => None,
}
}
type TimeSelectorPaving = Paving1D<ExtendedTime, (RuleKind, Arc<str>)>;
impl TimeSelectorPaving {
fn set_time_range(&mut self, range: Range<ExtendedTime>, state: (RuleKind, Arc<str>)) {
let selector = EmptyPavingSelector.dim_front(vec![range]);
self.set(&selector, &state);
}
fn into_time_selector(mut self) -> TimeRules {
let mut result = Vec::new();
#[allow(clippy::type_complexity)]
let pop_priority_order: [Box<dyn Fn(&(RuleKind, Arc<str>)) -> bool>; _] = [
Box::new(|(kind, comment)| *kind == RuleKind::Open && comment.is_empty()),
Box::new(|(kind, _)| *kind == RuleKind::Open),
Box::new(|(kind, comment)| *kind == RuleKind::Unknown && comment.is_empty()),
Box::new(|(kind, _)| *kind == RuleKind::Unknown),
Box::new(|(_, comment)| !comment.is_empty()),
];
for pop_priority in pop_priority_order {
while let Some((state, selector)) = self.pop_filter(&pop_priority) {
let (ranges, _) = selector.into_unpack_front();
let spans = ranges
.into_iter()
.map(|range| TimeSpan {
range: Time::Fixed(range.start)..Time::Fixed(range.end),
open_end: false,
repeats: None,
})
.collect();
result.push((state, TimeSelector { spans }));
}
}
result
}
}