pub mod day;
pub mod time;
use std::fmt::Display;
use std::sync::Arc;
use crate::normalize::frame::Bounded;
use crate::normalize::paving::{Paving, Paving5D};
use crate::normalize::{canonical_to_seq, ruleseq_to_selector};
use crate::sorted_vec::UniqueSortedVec;
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct OpeningHoursExpression {
pub rules: Vec<RuleSequence>,
}
impl OpeningHoursExpression {
pub fn is_constant(&self) -> bool {
let Some(kind) = self.rules.last().map(|rs| rs.kind) else {
return true;
};
let search_tail_full = self.rules.iter().rev().find(|rs| {
rs.day_selector.is_empty() || !rs.time_selector.is_00_24() || rs.kind != kind
});
let Some(tail) = search_tail_full else {
return kind == RuleKind::Closed;
};
tail.kind == kind && tail.is_constant()
}
pub fn normalize(self) -> Self {
let mut rules_queue = self.rules.into_iter().peekable();
let mut paving = Paving5D::default();
while let Some(rule) = rules_queue.peek() {
if rule.operator == RuleOperator::Fallback {
break;
}
let Some(selector) = ruleseq_to_selector(rule) else {
break;
};
let rule = rules_queue.next().unwrap();
if rule.operator == RuleOperator::Normal && rule.kind != RuleKind::Closed {
let (_, day_selector) = selector.clone().into_unpack_front();
let full_day_selector = day_selector.dim_front([Bounded::bounds()]);
paving.set(&full_day_selector, &Default::default());
}
paving.set(&selector, &(rule.kind, rule.comments));
}
Self {
rules: canonical_to_seq(paving).chain(rules_queue).collect(),
}
}
}
impl Display for OpeningHoursExpression {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Some(first) = self.rules.first() else {
return write!(f, "closed");
};
write!(f, "{first}")?;
for rule in &self.rules[1..] {
let separator = match rule.operator {
RuleOperator::Normal => "; ",
RuleOperator::Additional => ", ",
RuleOperator::Fallback => " || ",
};
write!(f, "{separator}")?;
rule.display(f, rule.operator == RuleOperator::Additional)?;
}
Ok(())
}
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct RuleSequence {
pub day_selector: day::DaySelector,
pub time_selector: time::TimeSelector,
pub kind: RuleKind,
pub operator: RuleOperator,
pub comments: UniqueSortedVec<Arc<str>>,
}
impl RuleSequence {
pub fn is_constant(&self) -> bool {
self.day_selector.is_empty() && self.time_selector.is_00_24()
}
pub(crate) fn display(
&self,
f: &mut std::fmt::Formatter<'_>,
force_day_selector: bool,
) -> std::fmt::Result {
let mut is_empty = true;
if self.is_constant() {
is_empty = false;
write!(f, "24/7")?;
} else {
is_empty = is_empty && self.day_selector.is_empty();
self.day_selector.display(f, force_day_selector)?;
if !self.time_selector.is_00_24() {
if !is_empty {
write!(f, " ")?;
}
is_empty = is_empty && self.time_selector.is_00_24();
write!(f, "{}", self.time_selector)?;
}
}
if self.kind != RuleKind::Open {
if !is_empty {
write!(f, " ")?;
}
is_empty = false;
write!(f, "{}", self.kind)?;
}
if !self.comments.is_empty() {
if !is_empty {
write!(f, " ")?;
}
write!(f, "\"{}\"", self.comments.join(", "))?;
}
Ok(())
}
}
impl Display for RuleSequence {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.display(f, false)
}
}
#[derive(Copy, Clone, Debug, Default, Hash, Eq, Ord, PartialEq, PartialOrd)]
pub enum RuleKind {
Open,
#[default]
Closed,
Unknown,
}
impl RuleKind {
pub const fn as_str(self) -> &'static str {
match self {
Self::Open => "open",
Self::Closed => "closed",
Self::Unknown => "unknown",
}
}
}
impl Display for RuleKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum RuleOperator {
Normal,
Additional,
Fallback,
}