use std::borrow::Cow;
use phf::PhfHash;
use crate::{phfbin::Decodable, util};
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct KanjiString<'a>(pub Cow<'a, str>);
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct Readings(&'static [u8]);
pub struct ReadingsIter {
data: &'static [u8],
i: usize,
}
#[derive(Debug)]
pub enum Reading {
Simple {
hira: String,
},
Tail {
hira: String,
ch: u8,
},
Context {
hira: String,
ctx: String,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CharType {
Hiragana,
Katakana,
Kanji,
Whitespace,
LeadingPunct,
TrailingPunct,
JoiningPunct,
Numeric,
Other,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct KakasiResult {
pub hiragana: String,
pub romaji: String,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum IsJapanese {
False,
Maybe,
True,
}
impl KakasiResult {
pub(crate) fn new(cap: usize) -> Self {
Self {
hiragana: String::with_capacity(cap),
romaji: String::with_capacity(cap),
}
}
}
impl From<IsJapanese> for bool {
fn from(x: IsJapanese) -> Self {
x != IsJapanese::False
}
}
impl<'a> KanjiString<'a> {
pub fn new(s: &'a str) -> Self {
Self(Cow::Borrowed(s))
}
}
impl Decodable for KanjiString<'_> {
fn decode(data: &'static [u8]) -> Self {
KanjiString(Cow::Owned(
data.chunks_exact(2)
.map(|c| unsafe { char::from_u32_unchecked(((c[0] as u32) << 8) | c[1] as u32) })
.collect(),
))
}
}
impl PhfHash for KanjiString<'_> {
fn phf_hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.phf_hash(state)
}
}
impl Decodable for Readings {
fn decode(data: &'static [u8]) -> Self {
Self(data)
}
}
impl Readings {
pub fn iter(&self) -> Option<ReadingsIter> {
if self.0.is_empty() {
None
} else {
Some(ReadingsIter { data: self.0, i: 0 })
}
}
}
impl Iterator for ReadingsIter {
type Item = Reading;
fn next(&mut self) -> Option<Self::Item> {
let mut hira = String::new();
let mut ctx = String::new();
let mut tail: Option<u8> = None;
let mut read_ctx = false;
while self.i < self.data.len() {
let c = &self.data[self.i];
self.i += 1;
if c & 0x80 > 0 {
match c {
0x80 => {
read_ctx = true;
}
0xff => {
break;
}
_ => {
tail = Some(*c & 0x7f);
}
}
} else {
let h = match c {
0x7f => 'ー',
_ => (util::HIRAGANA.0 + *c as u32).try_into().unwrap(),
};
if read_ctx {
ctx.push(h);
} else {
hira.push(h);
}
}
}
if hira.is_empty() {
return None;
}
Some(match tail {
Some(tail) => Reading::Tail { hira, ch: tail },
None => {
if !ctx.is_empty() {
Reading::Context { hira, ctx }
} else {
Reading::Simple { hira }
}
}
})
}
}
impl CharType {
pub fn space_after(self) -> bool {
self != CharType::LeadingPunct && self != CharType::JoiningPunct
}
pub fn space_before(self) -> bool {
self != CharType::TrailingPunct && self != CharType::JoiningPunct
}
}
#[cfg(test)]
mod tests {
use crate::phfbin::PhfMap;
use super::{KanjiString, Readings};
#[test]
fn readings_iter() {
let dict = PhfMap::new(crate::util::KANJI_DICT);
let readings = dict
.get::<KanjiString, Readings>(KanjiString::new("会"))
.unwrap();
let res = readings.iter().unwrap().collect::<Vec<_>>();
dbg!(&res);
}
}