use crate::utils::prompt_analyzer::all_langs;
const MAX_BARE_WORDS: usize = 3;
pub fn normalize(text: &str) -> String {
let mut out = String::with_capacity(text.len());
let mut last_was_space = true;
for ch in text.chars() {
if ch.is_alphanumeric() || ch == '\'' {
out.extend(ch.to_lowercase());
last_was_space = false;
} else if !last_was_space {
out.push(' ');
last_was_space = true;
}
}
out.trim_end().to_string()
}
fn opens_with(text: &str, phrase: &str) -> bool {
text.strip_prefix(phrase)
.is_some_and(|rest| rest.is_empty() || rest.starts_with(' '))
}
fn strip_trailing_address(normalized: &str) -> &str {
let mut end = normalized.len();
'outer: loop {
let head = normalized[..end].trim_end();
if head.is_empty() {
return head;
}
for lang in all_langs() {
for term in &lang.stop_address {
let term = term.trim().to_lowercase();
if term.is_empty() {
continue;
}
if let Some(rest) = head.strip_suffix(&term)
&& (rest.is_empty() || rest.ends_with(' '))
{
if rest.trim_end().is_empty() {
return head;
}
end = rest.len();
continue 'outer;
}
}
}
return head;
}
}
pub fn is_stop_intent(text: &str) -> bool {
let full = normalize(text);
if full.is_empty() {
return false;
}
let normalized = strip_trailing_address(&full);
if normalized.is_empty() {
return false;
}
let word_count = normalized.split_whitespace().count();
for lang in all_langs() {
for phrase in &lang.stop_intent {
let phrase = normalize(phrase);
if phrase.is_empty() {
continue;
}
if phrase.contains(' ') {
if opens_with(normalized, &phrase) {
return true;
}
} else if normalized == phrase && word_count <= MAX_BARE_WORDS {
return true;
}
}
}
false
}
pub fn is_stop_command_or_intent(text: &str) -> bool {
let trimmed = text.trim();
let without_slash = trimmed.strip_prefix('/').unwrap_or(trimmed);
let without_mention = without_slash
.split_once('@')
.map_or(without_slash, |(head, _)| head);
is_stop_intent(without_mention)
}