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