Skip to main content

whichtime_sys/refiners/
merge_weekday_date.rs

1//! Merge weekday and date refiner - combines adjacent weekday and date results
2//!
3//! Handles patterns like:
4//! - "Sonntag 7.12.2014" -> single result with weekday and date
5//! - "Freitag 30.12.16" -> single result with weekday and date
6//! - "Sonntag, den 7. Dezember 2014" -> single result
7
8use crate::components::Component;
9use crate::context::ParsingContext;
10use crate::refiners::Refiner;
11use crate::results::ParsedResult;
12
13/// Refiner that merges weekday matches with adjacent explicit dates.
14pub struct MergeWeekdayDateRefiner;
15
16impl MergeWeekdayDateRefiner {
17    fn has_explicit_date(result: &ParsedResult) -> bool {
18        result.start.is_certain(Component::Month) && result.start.is_certain(Component::Day)
19    }
20
21    fn merge_results(
22        context: &ParsingContext,
23        first: &ParsedResult,
24        second: &ParsedResult,
25        weekday_source: &ParsedResult,
26        date_source: &ParsedResult,
27    ) -> ParsedResult {
28        let mut merged_components = date_source.start;
29
30        if let Some(weekday) = weekday_source.start.get(Component::Weekday) {
31            merged_components.assign(Component::Weekday, weekday);
32        }
33
34        let merged_end = if date_source.end.is_some() {
35            date_source.end
36        } else {
37            weekday_source.end
38        };
39
40        let range_start = first.index.min(second.index);
41        let range_end = first.end_index.max(second.end_index);
42
43        ParsedResult::new(
44            context.reference,
45            range_start,
46            &context.text[range_start..range_end],
47            merged_components,
48            merged_end,
49        )
50    }
51}
52
53impl Refiner for MergeWeekdayDateRefiner {
54    fn refine(&self, context: &ParsingContext, results: Vec<ParsedResult>) -> Vec<ParsedResult> {
55        if results.len() < 2 {
56            return results;
57        }
58
59        let mut merged = Vec::with_capacity(results.len());
60        let mut i = 0;
61
62        while i < results.len() {
63            let current = &results[i];
64
65            // Check if next result exists and could be merged
66            if i + 1 < results.len() {
67                let next = &results[i + 1];
68
69                // Check if they're adjacent (with optional whitespace/connectors)
70                let gap_start = current.end_index;
71                let gap_end = next.index;
72
73                if gap_end >= gap_start {
74                    let gap = if gap_end > gap_start {
75                        &context.text[gap_start..gap_end]
76                    } else {
77                        ""
78                    };
79                    let gap_trimmed = gap.trim();
80
81                    // Check for German connectors: ",", ", den", just whitespace
82                    let is_connectable = gap_trimmed.is_empty()
83                        || gap_trimmed == ","
84                        || gap_trimmed == ", den"
85                        || gap_trimmed == ",den"
86                        || gap_trimmed == "den"
87                        || gap_trimmed == "、"
88                        || gap_trimmed == ","
89                        || gap_trimmed == "の";
90
91                    if is_connectable {
92                        let current_has_weekday = current.start.get(Component::Weekday).is_some();
93                        let next_has_weekday = next.start.get(Component::Weekday).is_some();
94                        let current_has_date = Self::has_explicit_date(current);
95                        let next_has_date = Self::has_explicit_date(next);
96
97                        if current_has_weekday && next_has_date && !next_has_weekday {
98                            let merged_result =
99                                Self::merge_results(context, current, next, current, next);
100                            merged.push(merged_result);
101                            i += 2;
102                            continue;
103                        }
104
105                        if current_has_date && !current_has_weekday && next_has_weekday {
106                            let merged_result =
107                                Self::merge_results(context, current, next, next, current);
108                            merged.push(merged_result);
109                            i += 2;
110                            continue;
111                        }
112                    }
113                }
114            }
115
116            merged.push(results[i].clone());
117            i += 1;
118        }
119
120        merged
121    }
122}