whichtime_sys/parsers/fr/
weekday.rs1use crate::components::Component;
10use crate::context::ParsingContext;
11use crate::dictionaries::fr as dict;
12use crate::error::Result;
13use crate::parsers::Parser;
14use crate::results::ParsedResult;
15use crate::scanner::TokenType;
16use chrono::{Datelike, Duration};
17use fancy_regex::Regex;
18use std::sync::LazyLock;
19
20static PATTERN: LazyLock<Regex> = LazyLock::new(|| {
21 Regex::new(
22 r"(?i)(?P<weekday>lundi|mardi|mercredi|jeudi|vendredi|samedi|dimanche)(?:\s+(?P<modifier>prochain|dernier|passé|passe))?(?:\s+(?:à\s+)?(?P<hour>\d{1,2})(?:h(?P<minute>\d{2})?)?)?"
23 ).unwrap()
24});
25
26pub struct FRWeekdayParser;
28
29impl FRWeekdayParser {
30 pub fn new() -> Self {
31 Self
32 }
33}
34
35impl Parser for FRWeekdayParser {
36 fn name(&self) -> &'static str {
37 "FRWeekdayParser"
38 }
39
40 fn should_apply(&self, context: &ParsingContext) -> bool {
41 context.has_token_type(TokenType::Weekday)
42 }
43
44 fn parse(&self, context: &ParsingContext) -> Result<Vec<ParsedResult>> {
45 let mut results = Vec::new();
46 let ref_date = context.reference.instant;
47
48 let mut start = 0;
49 while start < context.text.len() {
50 let search_text = &context.text[start..];
51 let mat = match PATTERN.find(search_text) {
52 Ok(Some(m)) => m,
53 Ok(None) => break,
54 Err(_) => break,
55 };
56
57 let matched_text = mat.as_str();
58 let index = start + mat.start();
59
60 let caps = match PATTERN.captures(matched_text) {
61 Ok(Some(c)) => c,
62 Ok(None) => {
63 start = index + 1;
64 continue;
65 }
66 Err(_) => {
67 start = index + 1;
68 continue;
69 }
70 };
71
72 let weekday_str = caps
73 .name("weekday")
74 .map(|m| m.as_str().to_lowercase())
75 .unwrap_or_default();
76
77 let weekday = match dict::get_weekday(&weekday_str) {
78 Some(w) => w,
79 None => {
80 start = index + 1;
81 continue;
82 }
83 };
84
85 let modifier = caps.name("modifier").map(|m| m.as_str().to_lowercase());
86
87 let ref_weekday = ref_date.weekday().num_days_from_monday() as i32;
89 let target_weekday = match weekday as i32 {
91 0 => 6, n => n - 1, };
94
95 let days_diff = match modifier.as_deref() {
96 Some("prochain") => {
97 let diff = target_weekday - ref_weekday;
99 if diff <= 0 { diff + 7 } else { diff }
100 }
101 Some("dernier") | Some("passé") | Some("passe") => {
102 let diff = target_weekday - ref_weekday;
104 if diff >= 0 { diff - 7 } else { diff }
105 }
106 None | Some(_) => {
107 target_weekday - ref_weekday
111 }
112 };
113
114 let target_date = ref_date + Duration::days(days_diff as i64);
115
116 let mut components = context.create_components();
117 components.assign(Component::Year, target_date.year());
118 components.assign(Component::Month, target_date.month() as i32);
119 components.assign(Component::Day, target_date.day() as i32);
120 components.assign(Component::Weekday, weekday as i32);
121
122 if let Some(hour_match) = caps.name("hour") {
124 let hour: i32 = hour_match.as_str().parse().unwrap_or(0);
125 let minute: i32 = caps
126 .name("minute")
127 .and_then(|m| m.as_str().parse().ok())
128 .unwrap_or(0);
129 components.assign(Component::Hour, hour);
130 components.assign(Component::Minute, minute);
131 }
132
133 results.push(context.create_result(
134 index,
135 index + matched_text.len(),
136 components,
137 None,
138 ));
139
140 start = index + matched_text.len();
141 }
142
143 Ok(results)
144 }
145}
146
147impl Default for FRWeekdayParser {
148 fn default() -> Self {
149 Self::new()
150 }
151}