piper-phoneme-streaming 0.1.1

A high-performance Rust library for streaming Text-to-Phoneme (G2P) conversion.
Documentation
use crate::text_expand::{ExpandResult, ExpandTask, ExpandUnit};
use std::collections::VecDeque;

use super::number_to_words_vi;

pub struct ViNumberExpandTask;

impl ExpandTask for ViNumberExpandTask {
    fn expand(&self, queue: &VecDeque<ExpandUnit>) -> Option<ExpandResult> {
        if let Some(ExpandUnit::Number(num)) = queue.front()
            && let Ok(val) = num.parse::<u64>()
        {
            let words = number_to_words_vi(val);
            if !words.is_empty() {
                return Some(ExpandResult::Replace(
                    1,
                    words
                        .into_iter()
                        .map(|w| ExpandUnit::Word(w.into()))
                        .collect(),
                ));
            }
        }
        None
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_vi_number_expansion() {
        let task = ViNumberExpandTask;
        let mut queue = VecDeque::new();

        queue.push_back(ExpandUnit::Number("123".into()));
        let res = task.expand(&queue).unwrap();
        if let ExpandResult::Replace(_, units) = res {
            assert_eq!(
                units,
                vec![
                    ExpandUnit::Word("một".into()),
                    ExpandUnit::Word("trăm".into()),
                    ExpandUnit::Word("hai".into()),
                    ExpandUnit::Word("mươi".into()),
                    ExpandUnit::Word("ba".into()),
                ]
            );
        } else {
            panic!("Expected Replace result");
        }

        queue.clear();
        queue.push_back(ExpandUnit::Number("1005".into()));
        let res = task.expand(&queue).unwrap();
        if let ExpandResult::Replace(_, units) = res {
            assert_eq!(
                units,
                vec![
                    ExpandUnit::Word("một".into()),
                    ExpandUnit::Word("nghìn".into()),
                    ExpandUnit::Word("không".into()),
                    ExpandUnit::Word("trăm".into()),
                    ExpandUnit::Word("linh".into()),
                    ExpandUnit::Word("năm".into()),
                ]
            );
        } else {
            panic!("Expected Replace result for 1005");
        }

        queue.clear();
        queue.push_back(ExpandUnit::Number("21".into()));
        let res = task.expand(&queue).unwrap();
        if let ExpandResult::Replace(_, units) = res {
            assert_eq!(
                units,
                vec![
                    ExpandUnit::Word("hai".into()),
                    ExpandUnit::Word("mươi".into()),
                    ExpandUnit::Word("mốt".into()),
                ]
            );
        } else {
            panic!("Expected Replace result for 21");
        }
    }

    #[test]
    fn test_vi_number_zero() {
        let task = ViNumberExpandTask;
        let mut queue = VecDeque::new();
        queue.push_back(ExpandUnit::Number("0".into()));

        let res = task.expand(&queue).unwrap();
        if let ExpandResult::Replace(n, units) = res {
            assert_eq!(n, 1);
            assert_eq!(units, vec![ExpandUnit::Word("không".into())]);
        } else {
            panic!("Expected Replace result for zero");
        }
    }

    #[test]
    fn test_vi_number_non_number_returns_none() {
        let task = ViNumberExpandTask;
        let mut queue = VecDeque::new();
        queue.push_back(ExpandUnit::Word("xin".into()));

        let res = task.expand(&queue);
        assert!(res.is_none(), "Non-number input should return None");
    }
}