use unicode_normalization::UnicodeNormalization;
use unicode_normalization::char::is_combining_mark;
#[must_use]
pub fn fold(text: &str) -> String {
text.nfd()
.filter(|c| !is_combining_mark(*c))
.flat_map(char::to_lowercase)
.collect()
}
#[must_use]
pub fn tokens(term: &str) -> Vec<String> {
let folded = fold(term);
let mut words: Vec<String> = folded
.split(|c: char| !c.is_alphanumeric())
.filter(|w| !w.is_empty())
.map(str::to_owned)
.collect();
words.sort();
words.dedup();
words
}
#[must_use]
pub fn prefixes(query: &str) -> Vec<String> {
fold(query)
.split(|c: char| !c.is_alphanumeric())
.filter(|w| !w.is_empty())
.map(str::to_owned)
.collect()
}
#[cfg(test)]
mod tests {
use super::{fold, prefixes, tokens};
#[test]
fn folding_drops_case_and_diacritics() {
assert_eq!(fold("Ménière"), "meniere");
assert_eq!(fold("Straße"), "straße");
assert_eq!(fold("ÅNGSTRÖM"), "angstrom");
}
#[test]
fn tokens_are_distinct_sorted_words() {
assert_eq!(
tokens("Ménière's disease (disorder)"),
vec!["disease", "disorder", "meniere", "s"]
);
assert_eq!(tokens("Heart heart HEART"), vec!["heart"]);
assert_eq!(tokens(" -- "), Vec::<String>::new());
assert_eq!(tokens("COVID-19"), vec!["19", "covid"]);
}
#[test]
fn prefixes_keep_query_order() {
assert_eq!(prefixes("Hart fal"), vec!["hart", "fal"]);
assert_eq!(prefixes(" "), Vec::<String>::new());
}
}