pub mod cformat;
pub mod compare;
pub mod parse;
pub mod segment;
pub mod seq;
use crate::reading::{traits::AsReadingRef, Reading};
use parse::{
reading::FuriToReadingParser, unchecked::UncheckedFuriParser, FuriParser, FuriParserGen,
};
use segment::{AsSegment, Segment, SegmentRef};
use std::{
borrow::Borrow,
fmt::Display,
ops::{Deref, Range},
};
use self::{cformat::CodeFormatter, segment::encode::FuriEncoder, seq::FuriSequence};
#[derive(Clone, Copy, Hash, Default, Debug)]
#[cfg_attr(feature = "with_serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Furigana<T>(pub T);
impl<T> Furigana<T>
where
T: AsRef<str>,
{
#[inline]
pub fn new(furi: T) -> Result<Self, ()> {
if !FuriParser::check(&furi) {
return Err(());
}
Ok(Self::new_unchecked(furi))
}
#[inline]
pub fn is_empty(&self) -> bool {
self.raw().is_empty()
}
#[inline]
pub fn raw(&self) -> &str {
self.0.as_ref()
}
#[inline]
pub fn gen_parser(&self) -> FuriParserGen {
FuriParserGen::new(self.raw())
}
#[inline]
pub fn kana(&self) -> FuriToReadingParser {
FuriToReadingParser::new(self.raw(), true)
}
#[inline]
pub fn kana_str(&self) -> String {
self.kana().to_string()
}
#[inline]
pub fn kanji(&self) -> FuriToReadingParser {
FuriToReadingParser::new(self.raw(), false)
}
#[inline]
pub fn kanji_str(&self) -> String {
self.kanji().to_string()
}
#[inline]
pub fn has_kana(&self) -> bool {
self.gen_parser().any(|i| !i.1)
}
#[inline]
pub fn has_kanji(&self) -> bool {
self.gen_parser().any(|i| i.1)
}
#[inline]
pub fn contains_kanji(&self, kanji: char) -> bool {
self.raw().contains(kanji)
}
#[inline]
pub fn to_reading(&self) -> Reading {
let (kana, kanji) = FuriToReadingParser::parse_kanji_and_kana(self.raw());
Reading::new_raw(kana, kanji)
}
#[inline]
pub fn to_seq(&self) -> FuriSequence<SegmentRef> {
self.into()
}
#[inline]
pub fn kana_segments(&self) -> impl Iterator<Item = SegmentRef> {
self.gen_parser()
.filter(|i| !i.1)
.map(|i| SegmentRef::Kana(i.0))
}
#[inline]
pub fn kanji_segments(&self) -> impl Iterator<Item = SegmentRef> {
self.gen_parser()
.filter(|i| i.1)
.map(|i| UncheckedFuriParser::from_seg_str(i.0, i.1))
}
#[inline]
pub fn segments(&self) -> UncheckedFuriParser {
FuriParser::new(self.raw()).unchecked()
}
#[inline]
pub fn segment_count(&self) -> usize {
self.gen_parser().count()
}
#[inline]
pub fn as_segments(&self) -> Vec<Segment> {
self.segments().map(|i| i.to_owned()).collect()
}
#[inline]
pub fn segment_at(&self, pos: usize) -> Option<SegmentRef> {
self.gen_parser()
.nth(pos)
.map(|i| UncheckedFuriParser::from_seg_str(i.0, i.1))
}
#[inline]
pub fn segment_range(&self, r: Range<usize>) -> impl Iterator<Item = SegmentRef> {
self.gen_parser()
.skip(r.start)
.take(r.len())
.map(|i| UncheckedFuriParser::from_seg_str(i.0, i.1))
}
#[inline]
pub fn as_segments_ref(&self) -> Vec<SegmentRef> {
self.segments().collect()
}
pub fn replace_seg<SR, WR>(&self, src: SR, with: WR) -> Furigana<String>
where
SR: AsReadingRef,
WR: AsReadingRef,
{
let src = src.as_reading_ref();
let mut with_str: Option<String> = None;
let mut out_buf = String::with_capacity(self.raw().len());
for seg_str in self.gen_parser() {
let seg = UncheckedFuriParser::from_seg_str(seg_str.0, seg_str.1);
if seg.eq_reading(src) {
let with = with_str.get_or_insert_with(|| with.encode().into_inner());
out_buf.push_str(with);
} else {
out_buf.push_str(seg_str.0);
}
}
out_buf.shrink_to_fit();
Furigana(out_buf)
}
#[inline]
pub fn as_owned(&self) -> Furigana<String> {
Furigana(self.raw().to_string())
}
}
impl<T> Furigana<T> {
#[inline]
pub fn as_deref(&self) -> Furigana<&T::Target>
where
T: Deref,
T::Target: AsRef<str>,
{
Furigana(&self.0)
}
#[inline]
pub fn as_ref(&self) -> Furigana<&T> {
Furigana(&self.0)
}
#[inline]
pub fn into_inner(self) -> T {
self.0
}
#[inline]
pub fn new_unchecked(furi: T) -> Self {
Self(furi)
}
#[inline]
pub fn code_formatter(&self) -> CodeFormatter<T> {
CodeFormatter::new(self)
}
}
impl Furigana<String> {
#[inline]
pub fn push_segment<S>(&mut self, seg: S)
where
S: AsSegment,
{
self.0.push_str(&seg.encode());
}
pub fn push_str<S>(&mut self, seg: S) -> Result<(), ()>
where
S: AsRef<str>,
{
if !FuriParser::check(&seg) {
return Err(());
}
self.push_str_unchecked(seg);
Ok(())
}
#[inline]
pub fn push_str_unchecked<S>(&mut self, seg: S)
where
S: AsRef<str>,
{
self.0.push_str(seg.as_ref());
}
}
impl<T: AsSegment> From<FuriSequence<T>> for Furigana<String> {
#[inline]
fn from(value: FuriSequence<T>) -> Self {
value.encode()
}
}
impl<'a, T: AsRef<str>> Into<FuriSequence<SegmentRef<'a>>> for &'a Furigana<T> {
#[inline]
fn into(self) -> FuriSequence<SegmentRef<'a>> {
FuriSequence::from(self.segments().to_vec())
}
}
impl<T: AsRef<str>> From<T> for Furigana<T> {
#[inline]
fn from(value: T) -> Self {
Self(value)
}
}
impl<T: AsRef<str>> From<Furigana<T>> for Vec<Segment> {
#[inline]
fn from(value: Furigana<T>) -> Self {
value.as_segments()
}
}
impl<S: AsSegment> FromIterator<S> for Furigana<String> {
#[inline]
fn from_iter<I: IntoIterator<Item = S>>(iter: I) -> Self {
let mut buf = String::new();
FuriEncoder::new(&mut buf).extend(iter);
Furigana(buf)
}
}
impl<S: AsSegment> Extend<S> for Furigana<String> {
#[inline]
fn extend<I: IntoIterator<Item = S>>(&mut self, iter: I) {
let iter = iter.into_iter();
self.0.reserve(iter.size_hint().0 * 6);
for s in iter {
self.push_segment(s);
}
}
}
impl Into<String> for Furigana<String> {
#[inline]
fn into(self) -> String {
self.0
}
}
impl<T> Eq for Furigana<T> where T: AsRef<str> + PartialEq<T> {}
impl<T, S> PartialEq<Furigana<S>> for Furigana<T>
where
T: AsRef<str>,
S: AsRef<str>,
S: PartialEq<T>,
{
#[inline]
fn eq(&self, other: &Furigana<S>) -> bool {
self.0.as_ref() == other.0.as_ref()
}
}
impl<T: AsRef<str>> Display for Furigana<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.raw())
}
}
impl<T: AsRef<str>> Borrow<str> for Furigana<T> {
#[inline]
fn borrow(&self) -> &str {
self.0.as_ref()
}
}
impl<T: AsRef<str>> Borrow<T> for Furigana<T> {
#[inline]
fn borrow(&self) -> &T {
&self.0
}
}
impl<T: AsRef<str>> AsRef<str> for Furigana<T> {
#[inline]
fn as_ref(&self) -> &str {
self.raw()
}
}
impl<T: AsRef<str>> AsRef<T> for Furigana<T> {
#[inline]
fn as_ref(&self) -> &T {
&self.0
}
}
impl<T: AsRef<str>> PartialEq<String> for Furigana<T> {
#[inline]
fn eq(&self, other: &String) -> bool {
self.raw() == other
}
}
impl<T: AsRef<str>> PartialEq<&str> for Furigana<T> {
#[inline]
fn eq(&self, other: &&str) -> bool {
self.raw() == *other
}
}
impl<'a, T> IntoIterator for &'a Furigana<T>
where
T: AsRef<str>,
{
type Item = SegmentRef<'a>;
type IntoIter = UncheckedFuriParser<'a>;
#[inline(always)]
fn into_iter(self) -> Self::IntoIter {
self.segments()
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::reading::ReadingRef;
use criterion::black_box;
#[test]
fn test_furigana() {
let furi = Furigana::new_unchecked("[音楽|おん|がく]が[大好|だい|す]きです");
assert_eq!(furi.kanji().to_string(), "音楽が大好きです");
assert_eq!(furi.kana().to_string(), "おんがくがだいすきです");
assert_eq!(furi.kana_str(), furi.kana().to_string());
assert_eq!(furi.kanji_str(), furi.kanji().to_string());
assert_eq!(
furi.to_reading(),
ReadingRef::new_with_kanji("おんがくがだいすきです", "音楽が大好きです")
);
assert!(furi.has_kana());
assert!(furi.has_kanji());
assert!(furi.contains_kanji('音'));
assert!(!furi.contains_kanji('弱'));
assert_eq!(furi.segment_count(), 4);
assert_eq!(furi.segment_at(black_box(2)), furi.segments().nth(2));
assert_eq!(furi.segment_at(1), Some(SegmentRef::new_kana("が")));
assert_eq!(
furi.as_segments(),
vec![
SegmentRef::new_kanji("音楽", &["おん", "がく"]),
SegmentRef::new_kana("が"),
SegmentRef::new_kanji("大好", &["だい", "す"]),
SegmentRef::new_kana("きです")
]
);
assert_eq!(furi.as_segments(), furi.as_segments_ref());
assert_eq!(furi.as_ref().raw(), furi.raw());
let mut furi2 = furi.as_owned();
furi2.push_segment(SegmentRef::new_kana("よ"));
assert_eq!(
furi2.as_segments(),
vec![
SegmentRef::new_kanji("音楽", &["おん", "がく"]),
SegmentRef::new_kana("が"),
SegmentRef::new_kanji("大好", &["だい", "す"]),
SegmentRef::new_kana("きですよ")
]
);
furi2.push_str("ね").unwrap();
assert_eq!(
furi2.as_segments(),
vec![
SegmentRef::new_kanji("音楽", &["おん", "がく"]),
SegmentRef::new_kana("が"),
SegmentRef::new_kanji("大好", &["だい", "す"]),
SegmentRef::new_kana("きですよね")
]
);
assert_eq!(
furi2.segment_range(1..3).collect::<Vec<_>>(),
vec![
SegmentRef::new_kana("が"),
SegmentRef::new_kanji("大好", &["だい", "す"]),
]
);
assert_eq!(
furi.kanji_segments().collect::<Vec<_>>(),
furi.segments().filter(|i| i.is_kanji()).collect::<Vec<_>>()
);
assert_eq!(
furi.kana_segments().collect::<Vec<_>>(),
furi.segments().filter(|i| i.is_kana()).collect::<Vec<_>>()
);
}
#[test]
fn test_furigana2() {
let furi = Furigana::new("[2|][x|えっくす]+[1|]の[定義|てい|ぎ][域|いき]が[A|えい]=[[1|],[2|]]のとき、[f|えふ]の[値域|ち|いき]は[f|えふ]([A|えい]) = [[3|],[5|]]となる。").unwrap();
assert_eq!(
furi.kanji().to_string(),
"2x+1の定義域がA=[1,2]のとき、fの値域はf(A) = [3,5]となる。"
);
}
#[test]
fn test_furigana3() {
let furi = Furigana("[2|][x|えっくす]+[1|]の[定義|てい|ぎ][域|いき]が[A|えい]=[[1|],[2|]]のとき、[f|えふ]の[値域|ち|いき]は[f|えふ]([A|えい]) = [[3|],[5|]]となる。".to_string());
let furi = furi.as_deref();
assert_eq!(
furi.kanji().to_string(),
"2x+1の定義域がA=[1,2]のとき、fの値域はf(A) = [3,5]となる。"
);
}
#[test]
fn test_replace_seg() {
let new = Furigana::new_unchecked("[音楽|おん|がく]が[大好|だい|す]きです")
.replace_seg(("おんがく", "音楽"), "セックス");
assert_eq!(new, Furigana("セックスが[大好|だい|す]きです"))
}
#[test]
fn test_is_empty() {
assert!(Furigana("").is_empty())
}
}