pub mod day;
pub mod time;
use alloc::str::FromStr;
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::fmt::Display;
use crate::normalize::frame::Bounded;
use crate::normalize::paving::{Paving, Paving5D, UnpackFromBack};
use crate::normalize::{canonical_to_seq, ruleseq_to_selector};
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct OpeningHoursExpression {
pub rules: Vec<RuleSequence>,
}
impl OpeningHoursExpression {
pub fn is_constant(&self) -> bool {
let Some(state) = self.rules.last().map(|rs| rs.as_state()) 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.as_state() != state
});
let Some(tail) = search_tail_full else {
return state == Default::default();
};
tail.as_state() == state && tail.is_constant()
}
pub fn normalize(self) -> Self {
let mut rules_queue = self.rules.into_iter().peekable();
let mut paving = Paving5D::default();
#[allow(clippy::result_large_err)]
while let Some((rule, selector)) = rules_queue.next_if_map(|rule| {
if rule.operator == RuleOperator::Fallback {
return Err(rule);
}
let Some(selector) = ruleseq_to_selector(&rule) else {
return Err(rule);
};
Ok((rule, selector))
}) {
if rule.operator == RuleOperator::Normal && rule.kind != RuleKind::Closed {
let mut full_day_selector = selector.clone();
full_day_selector.substitute_back([Bounded::bounds()]);
paving.set(&full_day_selector, &Default::default());
}
paving.set(&selector, &(rule.kind, rule.comment));
}
Self {
rules: canonical_to_seq(paving).chain(rules_queue).collect(),
}
}
}
impl Display for OpeningHoursExpression {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::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 comment: Arc<str>,
}
impl RuleSequence {
pub fn is_constant(&self) -> bool {
self.day_selector.is_empty() && self.time_selector.is_00_24()
}
pub fn as_state(&self) -> (RuleKind, &str) {
(self.kind, &self.comment)
}
pub(crate) fn display(
&self,
f: &mut core::fmt::Formatter<'_>,
force_day_selector: bool,
) -> core::fmt::Result {
let mut is_empty;
if self.is_constant() {
is_empty = false;
write!(f, "24/7")?;
} else {
self.day_selector.display(f, force_day_selector)?;
is_empty = !force_day_selector && self.day_selector.is_empty();
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.comment.is_empty() {
if !is_empty {
write!(f, " ")?;
}
write!(f, "\"{}\"", self.comment)?;
}
Ok(())
}
}
impl Display for RuleSequence {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::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 FromStr for RuleKind {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"open" => Ok(Self::Open),
"closed" => Ok(Self::Closed),
"unknown" => Ok(Self::Unknown),
other => Err(format!("Unknown rule kind {other:?}")),
}
}
}
impl Display for RuleKind {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum RuleOperator {
Normal,
Additional,
Fallback,
}