synd_term/
matcher.rs

1use std::{cell::RefCell, fmt, rc::Rc};
2
3use nucleo::{
4    pattern::{AtomKind, CaseMatching, Normalization, Pattern},
5    Utf32Str,
6};
7
8#[derive(Clone)]
9pub struct Matcher {
10    matcher: Rc<RefCell<nucleo::Matcher>>,
11    needle: Option<Pattern>,
12    // For Utf32 conversion
13    buf: Rc<RefCell<Vec<char>>>,
14}
15
16impl Default for Matcher {
17    fn default() -> Self {
18        Self::new()
19    }
20}
21
22impl Matcher {
23    pub fn new() -> Self {
24        Self {
25            matcher: Rc::new(RefCell::new(nucleo::Matcher::default())),
26            needle: None,
27            buf: Rc::new(RefCell::new(Vec::new())),
28        }
29    }
30
31    pub fn update_needle(&mut self, needle: &str) {
32        if needle.is_empty() {
33            self.needle = None;
34        } else {
35            let needle = Pattern::new(
36                needle,
37                CaseMatching::Smart,
38                Normalization::Smart,
39                AtomKind::Substring,
40            );
41            self.needle = Some(needle);
42        }
43    }
44
45    pub fn r#match(&self, haystack: &str) -> bool {
46        match self.needle.as_ref() {
47            Some(needle) => {
48                let mut buf = self.buf.borrow_mut();
49                let haystack = Utf32Str::new(haystack, &mut buf);
50                needle
51                    .score(haystack, &mut self.matcher.borrow_mut())
52                    .unwrap_or(0)
53                    > 0
54            }
55            None => true,
56        }
57    }
58}
59
60impl fmt::Debug for Matcher {
61    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62        f.debug_struct("Matcher")
63            .field("needle", &self.needle)
64            .finish_non_exhaustive()
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71
72    #[test]
73    fn should_match() {
74        let mut m = Matcher::new();
75
76        let cases = vec![
77            ("rustsec", "rustsec"),
78            ("rustsec", "RUSTSEC"),
79            ("rustsec", "RustSec"),
80            ("this week in rust", "This Week in Rust"),
81        ];
82
83        for case in cases {
84            m.update_needle(case.0);
85            assert!(m.r#match(case.1));
86        }
87    }
88}