1use winnow::{
2 Parser,
3 ascii::Caseless,
4 combinator::{alt, delimited, opt, preceded},
5 token::take_while,
6};
7
8use crate::{
9 DayReference, DayTime, Direction, LanguageParser, RelativeTime, Result, StandardDate, Time,
10 TimeExpression, TimeUnit, Weekday, WeekdayModifier, common, error::ParseErrorExt,
11};
12
13pub struct GermanParser;
19
20impl GermanParser {
21 fn parse_number(input: &mut &str) -> winnow::Result<i64> {
22 alt((
23 common::parse_digit_number,
24 "einem".value(1),
25 "einer".value(1),
26 "einen".value(1),
27 "eine".value(1),
28 "ein".value(1),
29 "zwei".value(2),
30 "drei".value(3),
31 "vier".value(4),
32 "fünf".value(5),
33 "sechs".value(6),
34 "sieben".value(7),
35 "acht".value(8),
36 "neun".value(9),
37 "zehn".value(10),
38 ))
39 .parse_next(input)
40 }
41
42 fn parse_time_unit(input: &mut &str) -> winnow::Result<TimeUnit> {
43 alt((
44 alt((
45 "Sekunden".value(TimeUnit::Second),
46 "Sekunde".value(TimeUnit::Second),
47 Caseless("sek").value(TimeUnit::Second), )),
49 alt((
50 "Minuten".value(TimeUnit::Minute),
51 "Minute".value(TimeUnit::Minute),
52 Caseless("min").value(TimeUnit::Minute), )),
54 alt((
55 "Stunden".value(TimeUnit::Hour),
56 "Stunde".value(TimeUnit::Hour),
57 Caseless("std").value(TimeUnit::Hour), )),
59 alt((
60 "Tagen".value(TimeUnit::Day),
61 "Tage".value(TimeUnit::Day),
62 "Tag".value(TimeUnit::Day),
63 )),
64 alt((
65 "Wochen".value(TimeUnit::Week),
66 "Woche".value(TimeUnit::Week),
67 )),
68 alt((
69 "Monaten".value(TimeUnit::Month),
70 "Monate".value(TimeUnit::Month),
71 "Monat".value(TimeUnit::Month),
72 )),
73 alt((
74 "Jahren".value(TimeUnit::Year),
75 "Jahre".value(TimeUnit::Year),
76 "Jahr".value(TimeUnit::Year),
77 )),
78 ))
79 .parse_next(input)
80 }
81
82 fn parse_relative_past(input: &mut &str) -> winnow::Result<TimeExpression> {
83 preceded(
84 "vor",
85 preceded(
86 take_while(1.., ' '),
87 (
88 Self::parse_number,
89 take_while(1.., ' '),
90 Self::parse_time_unit,
91 ),
92 ),
93 )
94 .map(|(amount, _, unit)| {
95 TimeExpression::Relative(RelativeTime {
96 amount,
97 unit,
98 direction: Direction::Past,
99 })
100 })
101 .parse_next(input)
102 }
103
104 fn parse_relative_future(input: &mut &str) -> winnow::Result<TimeExpression> {
105 preceded(
106 "in",
107 preceded(
108 take_while(1.., ' '),
109 (
110 Self::parse_number,
111 take_while(1.., ' '),
112 Self::parse_time_unit,
113 ),
114 ),
115 )
116 .map(|(amount, _, unit)| {
117 TimeExpression::Relative(RelativeTime {
118 amount,
119 unit,
120 direction: Direction::Future,
121 })
122 })
123 .parse_next(input)
124 }
125
126 fn parse_now(input: &mut &str) -> winnow::Result<TimeExpression> {
127 Caseless("jetzt")
128 .value(TimeExpression::Now)
129 .parse_next(input)
130 }
131
132 fn parse_iso_datetime(input: &mut &str) -> winnow::Result<TimeExpression> {
133 common::parse_iso_datetime(input)
134 }
135
136 fn parse_weekday(input: &mut &str) -> winnow::Result<Weekday> {
137 alt((
138 alt((
139 "Montag".value(Weekday::Monday),
140 Caseless("mo").value(Weekday::Monday), )),
142 alt((
143 "Dienstag".value(Weekday::Tuesday),
144 Caseless("di").value(Weekday::Tuesday), )),
146 alt((
147 "Mittwoch".value(Weekday::Wednesday),
148 Caseless("mi").value(Weekday::Wednesday), )),
150 alt((
151 "Donnerstag".value(Weekday::Thursday),
152 Caseless("do").value(Weekday::Thursday), )),
154 alt((
155 "Freitag".value(Weekday::Friday),
156 Caseless("fr").value(Weekday::Friday), )),
158 alt((
159 "Samstag".value(Weekday::Saturday),
160 Caseless("sa").value(Weekday::Saturday), )),
162 alt((
163 "Sonntag".value(Weekday::Sunday),
164 Caseless("so").value(Weekday::Sunday), )),
166 ))
167 .parse_next(input)
168 }
169
170 fn parse_day_shortcuts(input: &mut &str) -> winnow::Result<DayReference> {
171 alt((
172 Caseless("heute").value(DayReference::Today),
173 Caseless("gestern").value(DayReference::Yesterday),
174 Caseless("morgen").value(DayReference::Tomorrow),
175 ))
176 .parse_next(input)
177 }
178
179 fn parse_weekday_modifier(input: &mut &str) -> winnow::Result<WeekdayModifier> {
180 alt((
181 alt((
182 "letzten".value(WeekdayModifier::Last),
183 "letzte".value(WeekdayModifier::Last),
184 )),
185 alt((
186 "nächsten".value(WeekdayModifier::Next),
187 "nächste".value(WeekdayModifier::Next),
188 )),
189 ))
190 .parse_next(input)
191 }
192
193 fn parse_modified_weekday(input: &mut &str) -> winnow::Result<DayReference> {
194 (
195 Self::parse_weekday_modifier,
196 take_while(1.., ' '),
197 Self::parse_weekday,
198 )
199 .map(|(modifier, _, day)| DayReference::Weekday {
200 day,
201 modifier: Some(modifier),
202 })
203 .parse_next(input)
204 }
205
206 fn parse_simple_weekday(input: &mut &str) -> winnow::Result<DayReference> {
207 Self::parse_weekday
208 .map(|day| DayReference::Weekday {
209 day,
210 modifier: None,
211 })
212 .parse_next(input)
213 }
214
215 fn parse_day_reference(input: &mut &str) -> winnow::Result<TimeExpression> {
216 alt((
217 Self::parse_day_shortcuts,
218 Self::parse_modified_weekday,
219 Self::parse_simple_weekday,
220 ))
221 .map(TimeExpression::Day)
222 .parse_next(input)
223 }
224
225 fn parse_time_digits(input: &mut &str) -> winnow::Result<(u8, u8, u8)> {
226 let hour = common::parse_two_digit_number(input)?;
227 ':'.parse_next(input)?;
228 let minute = common::parse_two_digit_number(input)?;
229 let second = opt(preceded(':', common::parse_two_digit_number))
230 .parse_next(input)?
231 .unwrap_or(0);
232
233 Ok((hour, minute, second))
234 }
235
236 fn parse_time(input: &mut &str) -> winnow::Result<TimeExpression> {
237 (
238 Self::parse_time_digits,
239 opt(preceded(take_while(1.., ' '), Caseless("uhr"))),
240 )
241 .map(|((hour, minute, second), _)| {
242 TimeExpression::Time(Time {
243 hour,
244 minute,
245 second,
246 meridiem: None, })
248 })
249 .parse_next(input)
250 }
251
252 fn parse_day_at_time(input: &mut &str) -> winnow::Result<TimeExpression> {
253 (
254 alt((
255 Self::parse_day_shortcuts,
256 Self::parse_modified_weekday,
257 Self::parse_simple_weekday,
258 )),
259 take_while(1.., ' '),
260 "um",
261 take_while(1.., ' '),
262 Self::parse_time_digits,
263 opt(preceded(take_while(1.., ' '), Caseless("uhr"))),
264 )
265 .map(|(day, _, _, _, time_digits, _)| {
266 TimeExpression::DayTime(DayTime {
267 day,
268 time: Time {
269 hour: time_digits.0,
270 minute: time_digits.1,
271 second: time_digits.2,
272 meridiem: None,
273 },
274 })
275 })
276 .parse_next(input)
277 }
278
279 fn parse_date_format(input: &mut &str) -> winnow::Result<TimeExpression> {
280 (
282 common::parse_two_digit_number,
283 '.',
284 common::parse_two_digit_number,
285 '.',
286 common::parse_four_digit_number,
287 )
288 .map(|(day, _, month, _, year)| TimeExpression::Date(StandardDate { day, month, year }))
289 .parse_next(input)
290 }
291}
292
293impl LanguageParser for GermanParser {
294 fn parse(&self, input: &str) -> Result<TimeExpression> {
295 delimited(
296 take_while(0.., ' '),
297 alt((
298 Self::parse_iso_datetime,
299 Self::parse_date_format,
300 Self::parse_day_at_time,
301 Self::parse_now,
302 Self::parse_day_reference,
303 Self::parse_time,
304 Self::parse_relative_past,
305 Self::parse_relative_future,
306 )),
307 take_while(0.., ' '),
308 )
309 .parse(input)
310 .map_err(|e| e.to_temps_error(input))
311 }
312}