dcbor_pattern/pattern/value/
date_pattern.rs1use std::ops::RangeInclusive;
2
3use dcbor::{Date, prelude::*};
4
5use crate::pattern::{Matcher, Path, Pattern, vm::Instr};
6
7#[derive(Debug, Clone)]
9pub enum DatePattern {
10 Any,
12 Value(Date),
14 Range(RangeInclusive<Date>),
16 Earliest(Date),
18 Latest(Date),
20 String(String),
22 Regex(regex::Regex),
25}
26
27impl PartialEq for DatePattern {
28 fn eq(&self, other: &Self) -> bool {
29 match (self, other) {
30 (DatePattern::Any, DatePattern::Any) => true,
31 (DatePattern::Value(a), DatePattern::Value(b)) => a == b,
32 (DatePattern::Range(a), DatePattern::Range(b)) => a == b,
33 (DatePattern::Earliest(a), DatePattern::Earliest(b)) => a == b,
34 (DatePattern::Latest(a), DatePattern::Latest(b)) => a == b,
35 (DatePattern::String(a), DatePattern::String(b)) => a == b,
36 (DatePattern::Regex(a), DatePattern::Regex(b)) => {
37 a.as_str() == b.as_str()
38 }
39 _ => false,
40 }
41 }
42}
43
44impl Eq for DatePattern {}
45
46impl std::hash::Hash for DatePattern {
47 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
48 match self {
49 DatePattern::Any => {
50 0u8.hash(state);
51 }
52 DatePattern::Value(date) => {
53 1u8.hash(state);
54 date.hash(state);
55 }
56 DatePattern::Range(range) => {
57 2u8.hash(state);
58 range.start().hash(state);
59 range.end().hash(state);
60 }
61 DatePattern::Earliest(date) => {
62 3u8.hash(state);
63 date.hash(state);
64 }
65 DatePattern::Latest(date) => {
66 4u8.hash(state);
67 date.hash(state);
68 }
69 DatePattern::String(iso_string) => {
70 5u8.hash(state);
71 iso_string.hash(state);
72 }
73 DatePattern::Regex(regex) => {
74 6u8.hash(state);
75 regex.as_str().hash(state);
77 }
78 }
79 }
80}
81
82impl DatePattern {
83 pub fn any() -> Self { DatePattern::Any }
85
86 pub fn value(date: Date) -> Self { DatePattern::Value(date) }
88
89 pub fn range(range: RangeInclusive<Date>) -> Self {
92 DatePattern::Range(range)
93 }
94
95 pub fn earliest(date: Date) -> Self { DatePattern::Earliest(date) }
98
99 pub fn latest(date: Date) -> Self { DatePattern::Latest(date) }
102
103 pub fn string(iso_string: impl Into<String>) -> Self {
106 DatePattern::String(iso_string.into())
107 }
108
109 pub fn regex(regex: regex::Regex) -> Self { DatePattern::Regex(regex) }
112}
113
114impl Matcher for DatePattern {
115 fn paths(&self, haystack: &CBOR) -> Vec<Path> {
116 if let CBORCase::Tagged(tag, _) = haystack.as_case() {
118 if tag.value() == 1 {
120 if let Ok(date) = Date::try_from(haystack.clone()) {
122 let is_hit = match self {
123 DatePattern::Any => true,
124 DatePattern::Value(expected_date) => {
125 date == *expected_date
126 }
127 DatePattern::Range(range) => range.contains(&date),
128 DatePattern::Earliest(earliest) => date >= *earliest,
129 DatePattern::Latest(latest) => date <= *latest,
130 DatePattern::String(expected_string) => {
131 date.to_string() == *expected_string
132 }
133 DatePattern::Regex(regex) => {
134 regex.is_match(&date.to_string())
135 }
136 };
137
138 if is_hit {
139 vec![vec![haystack.clone()]]
140 } else {
141 vec![]
142 }
143 } else {
144 vec![]
146 }
147 } else {
148 vec![]
150 }
151 } else {
152 vec![]
154 }
155 }
156
157 fn compile(
158 &self,
159 code: &mut Vec<Instr>,
160 literals: &mut Vec<Pattern>,
161 _captures: &mut Vec<String>,
162 ) {
163 let idx = literals.len();
164 literals.push(Pattern::Value(crate::pattern::ValuePattern::Date(
165 self.clone(),
166 )));
167 code.push(Instr::MatchPredicate(idx));
168 }
169}
170
171impl std::fmt::Display for DatePattern {
172 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
173 match self {
174 DatePattern::Any => write!(f, "date"),
175 DatePattern::Value(date) => write!(f, "date'{}'", date),
176 DatePattern::Range(range) => {
177 write!(f, "date'{}...{}'", range.start(), range.end())
178 }
179 DatePattern::Earliest(date) => write!(f, "date'{}...'", date),
180 DatePattern::Latest(date) => write!(f, "date'...{}'", date),
181 DatePattern::String(iso_string) => {
182 write!(f, "date'{}'", iso_string)
183 }
184 DatePattern::Regex(regex) => {
185 write!(f, "date'/{}/'", regex.as_str())
186 }
187 }
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194
195 #[test]
196 fn test_date_pattern_any() {
197 let date = Date::from_ymd(2023, 12, 25);
199 let cbor = CBOR::from(date);
200
201 let pattern = DatePattern::any();
202 let paths = pattern.paths(&cbor);
203 assert_eq!(paths.len(), 1);
204 assert_eq!(paths[0], vec![cbor.clone()]);
205
206 let text_cbor = CBOR::from("test");
208 let paths = pattern.paths(&text_cbor);
209 assert!(paths.is_empty());
210 }
211
212 #[test]
213 fn test_date_pattern_value() {
214 let date = Date::from_ymd(2023, 12, 25);
215 let cbor = CBOR::from(date.clone());
216
217 let pattern = DatePattern::value(date.clone());
219 let paths = pattern.paths(&cbor);
220 assert_eq!(paths.len(), 1);
221 assert_eq!(paths[0], vec![cbor.clone()]);
222
223 let other_date = Date::from_ymd(2024, 1, 1);
225 let pattern = DatePattern::value(other_date);
226 let paths = pattern.paths(&cbor);
227 assert!(paths.is_empty());
228 }
229
230 #[test]
231 fn test_date_pattern_range() {
232 let date1 = Date::from_ymd(2023, 12, 20);
233 let date2 = Date::from_ymd(2023, 12, 25);
234 let date3 = Date::from_ymd(2023, 12, 30);
235
236 let test_date = date2.clone();
237 let cbor = CBOR::from(test_date);
238
239 let pattern = DatePattern::range(date1.clone()..=date3.clone());
241 let paths = pattern.paths(&cbor);
242 assert_eq!(paths.len(), 1);
243
244 let early_date = Date::from_ymd(2023, 12, 10);
246 let _late_date = Date::from_ymd(2024, 1, 10);
247 let pattern = DatePattern::range(early_date..=date1);
248 let paths = pattern.paths(&cbor);
249 assert!(paths.is_empty());
250 }
251
252 #[test]
253 fn test_date_pattern_earliest() {
254 let early_date = Date::from_ymd(2023, 12, 20);
255 let test_date = Date::from_ymd(2023, 12, 25);
256 let cbor = CBOR::from(test_date);
257
258 let pattern = DatePattern::earliest(early_date);
260 let paths = pattern.paths(&cbor);
261 assert_eq!(paths.len(), 1);
262
263 let late_date = Date::from_ymd(2023, 12, 30);
265 let pattern = DatePattern::earliest(late_date);
266 let paths = pattern.paths(&cbor);
267 assert!(paths.is_empty());
268 }
269
270 #[test]
271 fn test_date_pattern_latest() {
272 let late_date = Date::from_ymd(2023, 12, 30);
273 let test_date = Date::from_ymd(2023, 12, 25);
274 let cbor = CBOR::from(test_date);
275
276 let pattern = DatePattern::latest(late_date);
278 let paths = pattern.paths(&cbor);
279 assert_eq!(paths.len(), 1);
280
281 let early_date = Date::from_ymd(2023, 12, 20);
283 let pattern = DatePattern::latest(early_date);
284 let paths = pattern.paths(&cbor);
285 assert!(paths.is_empty());
286 }
287
288 #[test]
289 fn test_date_pattern_iso8601() {
290 let date = Date::from_ymd(2023, 12, 25);
291 let cbor = CBOR::from(date.clone());
292 let iso_string = date.to_string();
293
294 let pattern = DatePattern::string(iso_string.clone());
296 let paths = pattern.paths(&cbor);
297 assert_eq!(paths.len(), 1);
298
299 let pattern = DatePattern::string("2024-01-01T00:00:00Z");
301 let paths = pattern.paths(&cbor);
302 assert!(paths.is_empty());
303 }
304
305 #[test]
306 fn test_date_pattern_regex() {
307 let date = Date::from_ymd(2023, 12, 25);
308 let cbor = CBOR::from(date);
309
310 let regex = regex::Regex::new(r"2023-.*").unwrap();
312 let pattern = DatePattern::regex(regex);
313 let paths = pattern.paths(&cbor);
314 assert_eq!(paths.len(), 1);
315
316 let regex = regex::Regex::new(r"2024-.*").unwrap();
318 let pattern = DatePattern::regex(regex);
319 let paths = pattern.paths(&cbor);
320 assert!(paths.is_empty());
321 }
322
323 #[test]
324 fn test_date_pattern_display() {
325 assert_eq!(DatePattern::any().to_string(), "date");
326
327 let date = Date::from_ymd(2023, 12, 25);
328 assert_eq!(
329 DatePattern::value(date.clone()).to_string(),
330 format!("date'{}'", date)
331 );
332
333 let date1 = Date::from_ymd(2023, 12, 20);
334 let date2 = Date::from_ymd(2023, 12, 30);
335 assert_eq!(
336 DatePattern::range(date1.clone()..=date2.clone()).to_string(),
337 format!("date'{}...{}'", date1, date2)
338 );
339
340 assert_eq!(
341 DatePattern::earliest(date.clone()).to_string(),
342 format!("date'{}...'", date)
343 );
344 assert_eq!(
345 DatePattern::latest(date.clone()).to_string(),
346 format!("date'...{}'", date)
347 );
348
349 assert_eq!(
350 DatePattern::string("2023-12-25T00:00:00Z").to_string(),
351 "date'2023-12-25T00:00:00Z'"
352 );
353
354 let regex = regex::Regex::new(r"2023-.*").unwrap();
355 assert_eq!(DatePattern::regex(regex).to_string(), "date'/2023-.*/'");
356 }
357}