use crate::data::{HETERONYM_TABLE, PINYIN_DATA};
use crate::{get_block_and_index, Pinyin, PinyinData};
use std::convert::TryFrom;
use std::str::Chars;
#[derive(Copy, Clone)]
pub struct PinyinMulti {
first: &'static PinyinData,
other_indexes: &'static [u16],
}
impl PinyinMulti {
pub fn count(self) -> usize {
self.other_indexes.len() + 1
}
pub fn get(self, idx: usize) -> Pinyin {
self.get_opt(idx).unwrap()
}
pub fn get_opt(self, idx: usize) -> Option<Pinyin> {
if idx == 0 {
return Some(Pinyin(self.first));
}
self.other_indexes
.get(usize::try_from(idx).unwrap() - 1)
.map(|i| Pinyin(&PINYIN_DATA[usize::try_from(*i).unwrap()]))
}
}
impl IntoIterator for PinyinMulti {
type Item = Pinyin;
type IntoIter = PinyinMultiIter;
fn into_iter(self) -> PinyinMultiIter {
PinyinMultiIter {
inner: self,
index: 0,
}
}
}
pub struct PinyinMultiIter {
inner: PinyinMulti,
index: usize,
}
impl Iterator for PinyinMultiIter {
type Item = Pinyin;
fn next(&mut self) -> Option<Pinyin> {
self.inner.get_opt(self.index).map(|pinyin| {
self.index += 1;
pinyin
})
}
}
pub trait ToPinyinMulti {
type Output;
fn to_pinyin_multi(&self) -> Self::Output;
}
impl ToPinyinMulti for char {
type Output = Option<PinyinMulti>;
fn to_pinyin_multi(&self) -> Option<PinyinMulti> {
get_block_and_index(*self).and_then(|(block, index)| {
let first = match usize::try_from(block.data[index]).unwrap() {
0 => return None,
idx => &PINYIN_DATA[idx],
};
let idx = usize::try_from(block.heteronym[index]).unwrap();
let other_indexes = HETERONYM_TABLE[idx];
Some(PinyinMulti {
first,
other_indexes,
})
})
}
}
impl<'a> ToPinyinMulti for &'a str {
type Output = PinyinMultiStrIter<'a>;
#[inline]
fn to_pinyin_multi(&self) -> Self::Output {
PinyinMultiStrIter(self.chars())
}
}
pub struct PinyinMultiStrIter<'a>(Chars<'a>);
impl<'a> Iterator for PinyinMultiStrIter<'a> {
type Item = Option<PinyinMulti>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|c| c.to_pinyin_multi())
}
}
#[cfg(test)]
mod tests {
#[cfg(feature = "with_tone")]
use crate::Pinyin;
use crate::{PinyinMulti, ToPinyinMulti};
fn zi() -> PinyinMulti {
'子'.to_pinyin_multi().expect("no pinyin?")
}
#[test]
fn pinyin_multi_count() {
assert_eq!(zi().count(), 2);
}
#[test]
#[cfg(feature = "with_tone")]
fn pinyin_multi_get_opt() {
assert_eq!(zi().get_opt(0).map(Pinyin::with_tone), Some("zi"));
assert_eq!(zi().get_opt(1).map(Pinyin::with_tone), Some("zǐ"));
assert_eq!(zi().get_opt(2).map(Pinyin::with_tone), None);
}
#[test]
#[cfg(feature = "with_tone")]
fn pinyin_multi_get() {
assert_eq!(zi().get(0).with_tone(), "zi");
assert_eq!(zi().get(1).with_tone(), "zǐ");
}
#[test]
#[should_panic]
fn pinyin_multi_get_panic() {
zi().get(2);
}
#[test]
#[cfg(feature = "with_tone")]
fn pinyin_multi_iter() {
let mut iter = zi().into_iter();
assert_eq!(iter.next().map(Pinyin::with_tone), Some("zi"));
assert_eq!(iter.next().map(Pinyin::with_tone), Some("zǐ"));
assert_eq!(iter.next().map(Pinyin::with_tone), None);
}
#[test]
#[cfg(feature = "with_tone")]
fn str_to_pinyin_multi() {
let actual = "还没"
.to_pinyin_multi()
.map(|multi| {
multi
.unwrap()
.into_iter()
.map(Pinyin::with_tone)
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();
let expected = vec![vec!["hái", "fú", "huán"], vec!["méi", "mò", "me"]];
assert_eq!(actual, expected);
}
}