Skip to main content

chrono_node/common/
abstract_refiners.rs

1//! upstream `common/abstractRefiners.ts`: `Filter` and `MergingRefiner`.
2//!
3//! Implement [`FilterRefiner`] and wrap it in [`Filtering`], or implement [`MergingRefiner`] and
4//! wrap it in [`Merging`]: `Box::new(Merging::new(MyRefiner))`.
5
6use crate::core::chrono::Refiner;
7use crate::core::context::ParsingContext;
8use crate::core::result::ParsingResult;
9
10/// upstream `Filter`: keeps the results for which `is_valid` is true.
11pub trait FilterRefiner: Send + Sync {
12    fn is_valid(&self, context: &ParsingContext, result: &ParsingResult) -> bool;
13}
14
15pub struct Filtering<F>(pub F);
16
17impl<F: FilterRefiner> Filtering<F> {
18    pub fn new(inner: F) -> Filtering<F> {
19        Filtering(inner)
20    }
21}
22
23impl<F: FilterRefiner> Refiner for Filtering<F> {
24    fn refine(&self, context: &ParsingContext, results: Vec<ParsingResult>) -> Vec<ParsingResult> {
25        results.into_iter().filter(|r| self.0.is_valid(context, r)).collect()
26    }
27}
28
29/// upstream `MergingRefiner`: merges consecutive results.
30pub trait MergingRefiner: Send + Sync {
31    fn should_merge_results(
32        &self,
33        text_between: &str,
34        current: &ParsingResult,
35        next: &ParsingResult,
36        context: &ParsingContext,
37    ) -> bool;
38
39    /// Takes both results by value: upstream may mutate them while merging.
40    fn merge_results(
41        &self,
42        text_between: &str,
43        current: ParsingResult,
44        next: ParsingResult,
45        context: &ParsingContext,
46    ) -> ParsingResult;
47}
48
49pub struct Merging<M>(pub M);
50
51impl<M: MergingRefiner> Merging<M> {
52    pub fn new(inner: M) -> Merging<M> {
53        Merging(inner)
54    }
55}
56
57fn floor_char_boundary(text: &str, i: usize) -> usize {
58    let mut i = i.min(text.len());
59    while !text.is_char_boundary(i) {
60        i -= 1;
61    }
62    i
63}
64
65/// JS `text.substring(start, end)` on byte offsets: arguments are swapped if reversed.
66pub fn js_substring(text: &str, start: usize, end: usize) -> &str {
67    let (a, b) = if start <= end { (start, end) } else { (end, start) };
68    &text[floor_char_boundary(text, a)..floor_char_boundary(text, b)]
69}
70
71impl<M: MergingRefiner> Refiner for Merging<M> {
72    fn refine(&self, context: &ParsingContext, results: Vec<ParsingResult>) -> Vec<ParsingResult> {
73        if results.len() < 2 {
74            return results;
75        }
76        let mut merged = Vec::new();
77        let mut iter = results.into_iter();
78        let mut current = iter.next().expect("at least two results");
79        for next in iter {
80            let text_between = js_substring(&context.text, current.index + current.text.len(), next.index).to_string();
81            if !self.0.should_merge_results(&text_between, &current, &next, context) {
82                merged.push(current);
83                current = next;
84            } else {
85                current = self.0.merge_results(&text_between, current, next, context);
86            }
87        }
88        merged.push(current);
89        merged
90    }
91}