use crate::text_expand::{ExpandResult, ExpandTask, ExpandUnit};
use std::collections::VecDeque;
pub struct ViDateExpandTask;
impl ExpandTask for ViDateExpandTask {
fn expand(&self, queue: &VecDeque<ExpandUnit>) -> Option<ExpandResult> {
if queue.len() < 3 {
if let Some(ExpandUnit::Number(_)) = queue.front() {
if queue.len() == 1 {
return Some(ExpandResult::Maybe);
}
if let Some(ExpandUnit::Mark('/') | ExpandUnit::Mark('-')) = queue.get(1) {
return Some(ExpandResult::Maybe);
}
}
return None;
}
let mut units_to_consume = 0;
let mut day = None;
let mut month = None;
let mut year = None;
if let (
Some(ExpandUnit::Number(d)),
Some(ExpandUnit::Mark('/' | '-')),
Some(ExpandUnit::Number(m)),
) = (queue.front(), queue.get(1), queue.get(2))
{
day = Some(d.clone());
month = Some(m.clone());
units_to_consume = 3;
if queue.len() >= 5 {
if let (Some(ExpandUnit::Mark('/' | '-')), Some(ExpandUnit::Number(y))) =
(queue.get(3), queue.get(4))
{
year = Some(y.clone());
units_to_consume = 5;
}
} else if queue.len() == 4
&& let Some(ExpandUnit::Mark('/' | '-')) = queue.get(3)
{
return Some(ExpandResult::Maybe);
}
}
if let (Some(d), Some(m)) = (day, month) {
let mut replacement = vec![
ExpandUnit::Word("ngày".into()),
ExpandUnit::Number(d),
ExpandUnit::Word("tháng".into()),
ExpandUnit::Number(m),
];
if let Some(y) = year {
replacement.push(ExpandUnit::Word("năm".into()));
replacement.push(ExpandUnit::Number(y));
}
return Some(ExpandResult::Replace(units_to_consume, replacement));
}
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vi_date_expansion() {
let task = ViDateExpandTask;
let mut queue = VecDeque::new();
queue.push_back(ExpandUnit::Number("24".into()));
queue.push_back(ExpandUnit::Mark('/'));
queue.push_back(ExpandUnit::Number("03".into()));
queue.push_back(ExpandUnit::Mark('/'));
queue.push_back(ExpandUnit::Number("2026".into()));
let res = task.expand(&queue).unwrap();
if let ExpandResult::Replace(n, units) = res {
assert_eq!(n, 5);
assert_eq!(units[0], ExpandUnit::Word("ngày".into()));
assert_eq!(units[1], ExpandUnit::Number("24".into()));
assert_eq!(units[2], ExpandUnit::Word("tháng".into()));
assert_eq!(units[3], ExpandUnit::Number("03".into()));
assert_eq!(units[4], ExpandUnit::Word("năm".into()));
assert_eq!(units[5], ExpandUnit::Number("2026".into()));
} else {
panic!("Expected Replace result");
}
}
#[test]
fn test_vi_date_expansion_without_year() {
let task = ViDateExpandTask;
let mut queue = VecDeque::new();
queue.push_back(ExpandUnit::Number("15".into()));
queue.push_back(ExpandUnit::Mark('/'));
queue.push_back(ExpandUnit::Number("08".into()));
let res = task.expand(&queue).unwrap();
if let ExpandResult::Replace(n, units) = res {
assert_eq!(n, 3);
assert_eq!(units[0], ExpandUnit::Word("ngày".into()));
assert_eq!(units[1], ExpandUnit::Number("15".into()));
assert_eq!(units[2], ExpandUnit::Word("tháng".into()));
assert_eq!(units[3], ExpandUnit::Number("08".into()));
assert_eq!(units.len(), 4);
} else {
panic!("Expected Replace result for date without year");
}
}
#[test]
fn test_vi_date_dash_separator() {
let task = ViDateExpandTask;
let mut queue = VecDeque::new();
queue.push_back(ExpandUnit::Number("01".into()));
queue.push_back(ExpandUnit::Mark('-'));
queue.push_back(ExpandUnit::Number("01".into()));
queue.push_back(ExpandUnit::Mark('-'));
queue.push_back(ExpandUnit::Number("2025".into()));
let res = task.expand(&queue).unwrap();
if let ExpandResult::Replace(n, units) = res {
assert_eq!(n, 5);
assert_eq!(units[0], ExpandUnit::Word("ngày".into()));
} else {
panic!("Expected Replace result for dash-separated date");
}
}
#[test]
fn test_vi_date_maybe_single_number() {
let task = ViDateExpandTask;
let mut queue = VecDeque::new();
queue.push_back(ExpandUnit::Number("24".into()));
let res = task.expand(&queue);
assert!(
matches!(res, Some(ExpandResult::Maybe)),
"Single number should return Maybe"
);
}
#[test]
fn test_vi_date_maybe_number_separator() {
let task = ViDateExpandTask;
let mut queue = VecDeque::new();
queue.push_back(ExpandUnit::Number("24".into()));
queue.push_back(ExpandUnit::Mark('/'));
let res = task.expand(&queue);
assert!(
matches!(res, Some(ExpandResult::Maybe)),
"Number + '/' should return Maybe"
);
}
#[test]
fn test_vi_date_non_date_returns_none() {
let task = ViDateExpandTask;
let mut queue = VecDeque::new();
queue.push_back(ExpandUnit::Word("xin".into()));
let res = task.expand(&queue);
assert!(res.is_none(), "Non-date input should return None");
}
}