use crate::text_expand::{ExpandResult, ExpandTask, ExpandUnit};
use std::collections::VecDeque;
use super::{number_to_words_en, ones_en};
pub struct EnDecimalExpandTask;
impl ExpandTask for EnDecimalExpandTask {
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('.')) = queue.get(1) {
return Some(ExpandResult::Maybe);
}
}
return None;
}
match (queue.front(), queue.get(1), queue.get(2)) {
(
Some(ExpandUnit::Number(int_part)),
Some(ExpandUnit::Mark('.')),
Some(ExpandUnit::Number(frac_part)),
) => {
let int_val = int_part.parse::<u64>().ok()?;
let mut units: Vec<ExpandUnit> = number_to_words_en(int_val)
.into_iter()
.map(|w| ExpandUnit::Word(w.into()))
.collect();
units.push(ExpandUnit::Word("point".into()));
for ch in frac_part.chars() {
let digit = ch.to_digit(10)? as u64;
let word = ones_en(digit);
if !word.is_empty() {
units.push(ExpandUnit::Word(word.into()));
} else {
units.push(ExpandUnit::Word("zero".into()));
}
}
Some(ExpandResult::Replace(3, units))
}
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_en_decimal_expansion() {
let task = EnDecimalExpandTask;
let mut queue = VecDeque::new();
queue.push_back(ExpandUnit::Number("3".into()));
queue.push_back(ExpandUnit::Mark('.'));
queue.push_back(ExpandUnit::Number("14".into()));
let res = task.expand(&queue).unwrap();
if let ExpandResult::Replace(n, units) = res {
assert_eq!(n, 3);
assert_eq!(
units,
vec![
ExpandUnit::Word("three".into()),
ExpandUnit::Word("point".into()),
ExpandUnit::Word("one".into()),
ExpandUnit::Word("four".into()),
]
);
} else {
panic!("Expected Replace result");
}
}
#[test]
fn test_en_decimal_zero_fractional() {
let task = EnDecimalExpandTask;
let mut queue = VecDeque::new();
queue.push_back(ExpandUnit::Number("1".into()));
queue.push_back(ExpandUnit::Mark('.'));
queue.push_back(ExpandUnit::Number("0".into()));
let res = task.expand(&queue).unwrap();
if let ExpandResult::Replace(n, units) = res {
assert_eq!(n, 3);
assert_eq!(
units,
vec![
ExpandUnit::Word("one".into()),
ExpandUnit::Word("point".into()),
ExpandUnit::Word("zero".into()),
]
);
} else {
panic!("Expected Replace result for 1.0");
}
}
#[test]
fn test_en_decimal_maybe_single_number() {
let task = EnDecimalExpandTask;
let mut queue = VecDeque::new();
queue.push_back(ExpandUnit::Number("3".into()));
let res = task.expand(&queue);
assert!(
matches!(res, Some(ExpandResult::Maybe)),
"Single number should return Maybe"
);
}
#[test]
fn test_en_decimal_maybe_number_dot() {
let task = EnDecimalExpandTask;
let mut queue = VecDeque::new();
queue.push_back(ExpandUnit::Number("3".into()));
queue.push_back(ExpandUnit::Mark('.'));
let res = task.expand(&queue);
assert!(
matches!(res, Some(ExpandResult::Maybe)),
"Number + '.' should return Maybe"
);
}
#[test]
fn test_en_decimal_non_decimal_returns_none() {
let task = EnDecimalExpandTask;
let mut queue = VecDeque::new();
queue.push_back(ExpandUnit::Word("hello".into()));
let res = task.expand(&queue);
assert!(res.is_none(), "Non-decimal input should return None");
}
}