use std::collections::HashSet;
lazy_static::lazy_static! {
static ref STOP_WORDS: HashSet<&'static str> = {
let mut set = HashSet::new();
for word in &["the", "a", "an", "and", "or", "but", "in", "on", "at", "to", "for"] {
set.insert(*word);
}
set
};
}
pub fn tokenize_code(text: &str) -> Vec<String> {
let mut tokens = Vec::new();
let mut current = String::new();
for ch in text.chars() {
if ch.is_alphanumeric() || ch == '_' {
current.push(ch);
} else if !current.is_empty() {
add_split_tokens(¤t, &mut tokens);
current.clear();
}
}
if !current.is_empty() {
add_split_tokens(¤t, &mut tokens);
}
tokens.retain(|t| !STOP_WORDS.contains(t.as_str()) && t.len() > 1);
tokens
}
fn add_split_tokens(word: &str, tokens: &mut Vec<String>) {
let mut current = String::new();
let chars: Vec<char> = word.chars().collect();
for i in 0..chars.len() {
let ch = chars[i];
if ch == '_' {
if !current.is_empty() {
tokens.push(current.clone());
current.clear();
}
} else if i > 0 {
let prev = chars[i - 1];
let is_case_boundary =
(prev.is_lowercase() && ch.is_uppercase()) ||
(i + 1 < chars.len() && prev.is_uppercase() && ch.is_uppercase() && chars[i + 1].is_lowercase());
if is_case_boundary {
if !current.is_empty() {
tokens.push(current.clone());
current.clear();
}
}
current.push(ch.to_ascii_lowercase());
} else {
current.push(ch.to_ascii_lowercase());
}
}
if !current.is_empty() {
tokens.push(current);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_camel_case() {
let tokens = tokenize_code("getUserName");
assert_eq!(tokens, vec!["get", "user", "name"]);
}
#[test]
fn test_snake_case() {
let tokens = tokenize_code("get_user_name");
assert_eq!(tokens, vec!["get", "user", "name"]);
}
#[test]
fn test_mixed() {
let tokens = tokenize_code("handleHTTPRequest");
assert_eq!(tokens, vec!["handle", "http", "request"]);
}
}