#![allow(dead_code)]
pub fn extract_tab_mention(message: &str) -> Option<usize> {
let bytes = message.as_bytes();
let mut i = 0;
while i < bytes.len() {
if bytes[i] == b'@' {
if i > 0 && !is_mention_boundary(bytes[i - 1]) {
i += 1;
continue;
}
if i + 4 <= bytes.len()
&& bytes[i..i + 4].eq_ignore_ascii_case(b"@tab")
&& i + 4 < bytes.len()
&& bytes[i + 4].is_ascii_digit()
{
let num_start = i + 4;
let num_end = scan_digits(bytes, num_start);
if let Some(num) = parse_usize(&bytes[num_start..num_end])
&& num > 0
&& is_mention_terminator(bytes, num_end)
{
return Some(num);
}
}
if i + 1 < bytes.len() && bytes[i + 1].is_ascii_digit() {
let num_start = i + 1;
let num_end = scan_digits(bytes, num_start);
if let Some(num) = parse_usize(&bytes[num_start..num_end])
&& num > 0
&& is_mention_terminator(bytes, num_end)
{
return Some(num);
}
}
}
i += 1;
}
None
}
pub fn extract_all_tab_mentions(message: &str) -> Vec<usize> {
let mut mentions = Vec::new();
let bytes = message.as_bytes();
let mut i = 0;
while i < bytes.len() {
if bytes[i] == b'@' && (i == 0 || is_mention_boundary(bytes[i - 1])) {
let token_start = i;
let mut j = i + 1;
if j + 3 <= bytes.len() && bytes[j..j + 3].eq_ignore_ascii_case(b"tab") {
j += 3;
}
let num_start = j;
while j < bytes.len() && bytes[j].is_ascii_digit() {
j += 1;
}
let num_end = j;
if num_start < num_end
&& is_mention_terminator(bytes, num_end)
&& let Some(num) = parse_usize(&bytes[num_start..num_end])
&& num > 0
{
mentions.push(num);
i = num_end;
continue;
}
let _ = token_start; }
i += 1;
}
mentions
}
fn is_mention_boundary(b: u8) -> bool {
b == b' ' || b == b'\t' || b == b'\n' || b == b'\r' || b == b',' || b == b';'
}
fn is_mention_terminator(bytes: &[u8], pos: usize) -> bool {
pos >= bytes.len()
|| bytes[pos] == b' '
|| bytes[pos] == b'\t'
|| bytes[pos] == b'\n'
|| bytes[pos] == b'\r'
|| bytes[pos] == b','
|| bytes[pos] == b'.'
|| bytes[pos] == b'!'
|| bytes[pos] == b'?'
|| bytes[pos] == b';'
|| bytes[pos] == b':'
}
fn scan_digits(bytes: &[u8], start: usize) -> usize {
let mut end = start;
while end < bytes.len() && bytes[end].is_ascii_digit() {
end += 1;
}
end
}
fn parse_usize(s: &[u8]) -> Option<usize> {
if s.is_empty() {
return None;
}
let mut result: usize = 0;
for &b in s {
if !b.is_ascii_digit() {
return None;
}
result = result.checked_mul(10)?;
result = result.checked_add((b - b'0') as usize)?;
}
Some(result)
}
pub fn resolve_tab_mention<'a, I>(tab_number: usize, tab_ids: I) -> Option<u64>
where
I: IntoIterator<Item = &'a u64>,
{
let ids: Vec<u64> = tab_ids.into_iter().copied().collect();
if tab_number == 0 || tab_number > ids.len() {
return None;
}
Some(ids[tab_number - 1])
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_extract_tab_mention() {
assert_eq!(extract_tab_mention("Hello @Tab2!"), Some(2));
assert_eq!(extract_tab_mention("see @3 please"), Some(3));
assert_eq!(extract_tab_mention("@Tab1 help"), Some(1));
assert_eq!(extract_tab_mention("no mention here"), None);
assert_eq!(extract_tab_mention("email@2 is wrong"), None); assert_eq!(extract_tab_mention("@0 invalid"), None); assert_eq!(extract_tab_mention(""), None);
}
#[test]
fn test_extract_case_insensitive() {
assert_eq!(extract_tab_mention("Hello @TAB2!"), Some(2));
assert_eq!(extract_tab_mention("see @tab3 please"), Some(3));
}
#[test]
fn test_extract_with_punctuation() {
assert_eq!(extract_tab_mention("Please @Tab2, review"), Some(2));
assert_eq!(extract_tab_mention("Ask @Tab2."), Some(2));
assert_eq!(extract_tab_mention("Hey @Tab2!"), Some(2));
assert_eq!(extract_tab_mention("What about @Tab2?"), Some(2));
}
#[test]
fn test_extract_all_mentions() {
let mentions = extract_all_tab_mentions("Hey @Tab2 and @3, also @Tab1");
assert_eq!(mentions, vec![2, 3, 1]);
}
#[test]
fn test_extract_mention_at_start() {
assert_eq!(extract_tab_mention("@Tab1 hi"), Some(1));
}
#[test]
fn test_resolve_tab_mention() {
let tab_ids = [100, 50, 200];
assert_eq!(resolve_tab_mention(1, tab_ids.iter()), Some(100));
assert_eq!(resolve_tab_mention(2, tab_ids.iter()), Some(50));
assert_eq!(resolve_tab_mention(3, tab_ids.iter()), Some(200));
assert_eq!(resolve_tab_mention(4, tab_ids.iter()), None);
assert_eq!(resolve_tab_mention(0, tab_ids.iter()), None);
}
}