use nucleo::pattern::{AtomKind, CaseMatching, Normalization, Pattern};
use nucleo::{Config, Matcher, Utf32String};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum MatchMode {
#[default]
Fuzzy,
Substring,
Exact,
}
impl MatchMode {
pub fn label(self) -> &'static str {
match self {
MatchMode::Fuzzy => "Mode: FUZZY",
MatchMode::Substring => "Mode: SUBSTRING",
MatchMode::Exact => "Mode: EXACT",
}
}
pub(crate) fn atom_kind(self) -> AtomKind {
match self {
MatchMode::Fuzzy => AtomKind::Fuzzy,
MatchMode::Substring => AtomKind::Substring,
MatchMode::Exact => AtomKind::Exact,
}
}
}
pub(crate) struct NucleoMatcher {
matcher: Matcher,
cached_query: String,
cached_mode: MatchMode,
cached_case: Option<CaseMatching>,
cached_norm: Option<Normalization>,
cached_pattern: Option<Pattern>,
}
impl Default for NucleoMatcher {
fn default() -> Self {
Self {
matcher: Matcher::new(Config::DEFAULT),
cached_query: String::new(),
cached_mode: MatchMode::Fuzzy,
cached_case: None,
cached_norm: None,
cached_pattern: None,
}
}
}
impl NucleoMatcher {
pub(crate) fn match_indices(
&mut self,
haystack: &Utf32String,
query: &str,
mode: MatchMode,
case_matching: CaseMatching,
normalization: Normalization,
hits: &mut Vec<u32>,
) -> Option<u32> {
let needs_rebuild = self.cached_pattern.is_none()
|| self.cached_query != query
|| self.cached_mode != mode
|| self.cached_case != Some(case_matching)
|| self.cached_norm != Some(normalization);
if needs_rebuild {
self.cached_query.clear();
self.cached_query.push_str(query);
self.cached_mode = mode;
self.cached_case = Some(case_matching);
self.cached_norm = Some(normalization);
self.cached_pattern = Some(Pattern::new(
query,
case_matching,
normalization,
mode.atom_kind(),
));
}
self.cached_pattern
.as_ref()
.unwrap()
.indices(haystack.slice(..), &mut self.matcher, hits)
}
}