pub(crate) fn remove_non_alphanumeric(text: &str) -> String {
text
.chars()
.map(|c| {
if is_word(c) || c.is_whitespace() {
c
} else {
' '
}
})
.collect()
}
pub(crate) fn tokenize(text: &str) -> Vec<String> {
text
.to_lowercase()
.chars()
.map(|c| if is_word(c) { c } else { ' ' })
.collect::<String>()
.split_whitespace()
.map(str::to_string)
.collect()
}
fn is_word(c: char) -> bool {
c.is_alphanumeric() || c == '_'
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tokenizes_a_short_english_sentence() {
let cleaned = remove_non_alphanumeric("The quick brown fox jumps over the lazy dog.");
let tokens = tokenize(&cleaned);
assert_eq!(
tokens,
vec![
"the", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"
]
);
}
#[test]
fn a_lone_underscore_is_its_own_token() {
let cleaned = remove_non_alphanumeric("hello _ world");
assert_eq!(tokenize(&cleaned), vec!["hello", "_", "world"]);
}
#[test]
fn bare_punctuation_and_symbols_vanish_rather_than_becoming_tokens() {
let text = "fn main() { let xs: Vec<f32> = (0..10).map(|i| i as f32 * 0.5).collect(); }";
let cleaned = remove_non_alphanumeric(text);
let tokens = tokenize(&cleaned);
for symbol in ["<", ">", "=", "(", ")", "{", "}", ":", ";", "!", ".", ","] {
assert!(
!tokens.contains(&symbol.to_string()),
"{symbol} in {tokens:?}"
);
}
assert_eq!(
tokens,
vec![
"fn", "main", "let", "xs", "vec", "f32", "0", "10", "map", "i", "i", "as", "f32", "0", "5",
"collect"
]
);
}
#[test]
fn a_whitespace_free_script_is_one_undivided_token() {
let cleaned = remove_non_alphanumeric("東京タワーは高いです。");
assert_eq!(tokenize(&cleaned), vec!["東京タワーは高いです"]);
}
#[test]
fn accented_latin_words_lowercase_and_tokenize_as_single_words() {
let cleaned = remove_non_alphanumeric("Árvíztűrő tükörfúrógép");
assert_eq!(tokenize(&cleaned), vec!["árvíztűrő", "tükörfúrógép"]);
}
#[test]
fn empty_and_whitespace_only_text_tokenizes_to_nothing() {
assert!(tokenize(&remove_non_alphanumeric("")).is_empty());
assert!(tokenize(&remove_non_alphanumeric(" \n\t ")).is_empty());
}
#[test]
fn mixed_whitespace_runs_collapse_between_tokens() {
let cleaned = remove_non_alphanumeric("a \t\n b");
assert_eq!(tokenize(&cleaned), vec!["a", "b"]);
}
}