use nucleo_matcher::pattern::{CaseMatching, Normalization, Pattern};
use nucleo_matcher::{Matcher, Utf32Str};
const MIN_QUERY_LEN: usize = 2;
const MAX_RESULTS: usize = 50;
#[derive(Default)]
pub struct SearchState {
pub is_open: bool,
pub input: String,
matcher: Matcher,
pub matched_indices: Vec<usize>,
}
impl SearchState {
pub fn open(&mut self) {
self.is_open = true;
}
pub fn close(&mut self) {
self.is_open = false;
self.input.clear();
self.matched_indices.clear();
}
pub fn submit(&mut self) {
self.is_open = false;
}
pub fn handle_char(&mut self, c: char) {
self.input.push(c);
}
pub fn handle_backspace(&mut self) {
self.input.pop();
}
pub fn is_filtering(&self) -> bool {
self.input.len() >= MIN_QUERY_LEN
}
pub fn update_matches(&mut self, names: &[String]) {
self.matched_indices.clear();
if self.input.len() < MIN_QUERY_LEN {
return;
}
let pattern = Pattern::parse(&self.input, CaseMatching::Ignore, Normalization::Smart);
let mut buf = Vec::new();
let mut scored: Vec<(usize, u32)> = names
.iter()
.enumerate()
.filter_map(|(idx, name)| {
let haystack = Utf32Str::new(name, &mut buf);
let score = pattern.score(haystack, &mut self.matcher)?;
Some((idx, score))
})
.collect();
scored.sort_by(|a, b| b.1.cmp(&a.1));
scored.truncate(MAX_RESULTS);
self.matched_indices = scored.into_iter().map(|(idx, _)| idx).collect();
}
}