opening_hours_syntax/rules/
mod.rs1pub mod day;
2pub mod time;
3
4use std::fmt::Display;
5use std::sync::Arc;
6
7use crate::normalize::frame::Bounded;
8use crate::normalize::paving::{Paving, Paving5D};
9use crate::normalize::{canonical_to_seq, ruleseq_to_selector};
10use crate::sorted_vec::UniqueSortedVec;
11
12#[derive(Clone, Debug, Hash, PartialEq, Eq)]
15pub struct OpeningHoursExpression {
16 pub rules: Vec<RuleSequence>,
17}
18
19impl OpeningHoursExpression {
20 pub fn is_constant(&self) -> bool {
34 let Some(kind) = self.rules.last().map(|rs| rs.kind) else {
35 return true;
36 };
37
38 let search_tail_full = self.rules.iter().rev().find(|rs| {
40 rs.day_selector.is_empty() || !rs.time_selector.is_00_24() || rs.kind != kind
41 });
42
43 let Some(tail) = search_tail_full else {
44 return kind == RuleKind::Closed;
45 };
46
47 tail.kind == kind && tail.is_constant()
48 }
49
50 pub fn normalize(self) -> Self {
58 let mut rules_queue = self.rules.into_iter().peekable();
59 let mut paving = Paving5D::default();
60
61 while let Some(rule) = rules_queue.peek() {
62 if rule.operator == RuleOperator::Fallback {
63 break;
64 }
65
66 let Some(selector) = ruleseq_to_selector(rule) else {
67 break;
68 };
69
70 let rule = rules_queue.next().unwrap();
71
72 if rule.operator == RuleOperator::Normal && rule.kind != RuleKind::Closed {
75 let (_, day_selector) = selector.clone().into_unpack_front();
76 let full_day_selector = day_selector.dim_front([Bounded::bounds()]);
77 paving.set(&full_day_selector, &Default::default());
78 }
79
80 paving.set(&selector, &(rule.kind, rule.comments));
81 }
82
83 Self {
84 rules: canonical_to_seq(paving).chain(rules_queue).collect(),
85 }
86 }
87}
88
89impl Display for OpeningHoursExpression {
90 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91 let Some(first) = self.rules.first() else {
92 return write!(f, "closed");
93 };
94
95 write!(f, "{first}")?;
96
97 for rule in &self.rules[1..] {
98 let separator = match rule.operator {
99 RuleOperator::Normal => " ; ",
100 RuleOperator::Additional => ", ",
101 RuleOperator::Fallback => " || ",
102 };
103
104 write!(f, "{separator}")?;
105
106 rule.display(f, rule.operator == RuleOperator::Additional)?;
111 }
112
113 Ok(())
114 }
115}
116
117#[derive(Clone, Debug, Hash, PartialEq, Eq)]
120pub struct RuleSequence {
121 pub day_selector: day::DaySelector,
122 pub time_selector: time::TimeSelector,
123 pub kind: RuleKind,
124 pub operator: RuleOperator,
125 pub comments: UniqueSortedVec<Arc<str>>,
126}
127
128impl RuleSequence {
129 pub fn is_constant(&self) -> bool {
132 self.day_selector.is_empty() && self.time_selector.is_00_24()
133 }
134
135 pub(crate) fn display(
136 &self,
137 f: &mut std::fmt::Formatter<'_>,
138 force_day_selector: bool,
139 ) -> std::fmt::Result {
140 let mut is_empty = true;
141
142 if self.is_constant() {
143 is_empty = false;
144 write!(f, "24/7")?;
145 } else {
146 is_empty = is_empty && self.day_selector.is_empty();
147 self.day_selector.display(f, force_day_selector)?;
148
149 if !self.time_selector.is_00_24() {
150 if !is_empty {
151 write!(f, " ")?;
152 }
153
154 is_empty = is_empty && self.time_selector.is_00_24();
155 write!(f, "{}", self.time_selector)?;
156 }
157 }
158
159 if self.kind != RuleKind::Open {
160 if !is_empty {
161 write!(f, " ")?;
162 }
163
164 is_empty = false;
165 write!(f, "{}", self.kind)?;
166 }
167
168 if !self.comments.is_empty() {
169 if !is_empty {
170 write!(f, " ")?;
171 }
172
173 write!(f, "\"{}\"", self.comments.join(", "))?;
174 }
175
176 Ok(())
177 }
178}
179
180impl Display for RuleSequence {
181 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
182 self.display(f, false)
183 }
184}
185
186#[derive(Copy, Clone, Debug, Default, Hash, Eq, Ord, PartialEq, PartialOrd)]
189pub enum RuleKind {
190 Open,
191 #[default]
192 Closed,
193 Unknown,
194}
195
196impl RuleKind {
197 pub const fn as_str(self) -> &'static str {
198 match self {
199 Self::Open => "open",
200 Self::Closed => "closed",
201 Self::Unknown => "unknown",
202 }
203 }
204}
205
206impl Display for RuleKind {
207 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208 write!(f, "{}", self.as_str())
209 }
210}
211
212#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
215pub enum RuleOperator {
216 Normal,
217 Additional,
218 Fallback,
219}