use alloc::string::String;
use unicode_normalization::char::decompose_canonical;
use super::emit::emit_truncated;
use super::policy::TokenizerPolicy;
use super::tables;
use super::unicode::UnicodeBackend;
pub(super) fn fold_segment(
segment: &str,
token: &mut String,
canonical: &mut String,
policy: TokenizerPolicy,
unicode: &UnicodeBackend,
sink: &mut dyn FnMut(&str),
) {
token.clear();
let mut needs_nfkc_again = false;
for c in segment.chars() {
if fold_into(c, token, policy, &mut needs_nfkc_again) {
emit_folded(token, canonical, needs_nfkc_again, policy, unicode, sink);
token.clear();
needs_nfkc_again = false;
}
}
emit_folded(token, canonical, needs_nfkc_again, policy, unicode, sink);
}
#[inline]
fn is_ignorable_format(c: char) -> bool {
UnicodeBackend::is_default_ignorable(c)
}
fn fold_into(
c: char,
out: &mut String,
policy: TokenizerPolicy,
needs_nfkc_again: &mut bool,
) -> bool {
if policy.fold_russian_yo
&& let Some(mapped) = tables::mapped_char(c, tables::RUSSIAN_SEARCH_FOLDS)
{
out.push(mapped);
return false;
}
if is_ignorable_format(c) {
*needs_nfkc_again |= !out.is_empty();
return false;
}
if UnicodeBackend::is_mark(c) {
*needs_nfkc_again = true;
if !out.ends_with(|previous: char| previous.is_ascii_alphanumeric()) {
out.push(c);
}
return false;
}
if !c.is_alphanumeric() {
if out.ends_with(char::is_alphanumeric) && tables::is_word_joiner(c) {
out.push(c);
return false;
}
return !out.is_empty();
}
if c.is_ascii() {
out.push(c);
return false;
}
let mut parts = [char::MAX; 8];
let mut count = 0usize;
decompose_canonical(c, |decomposed| {
if count < parts.len() {
parts[count] = decomposed;
}
count += 1;
});
if policy.fold_latin_diacritics
&& count <= parts.len()
&& count > 0
&& parts[0].is_ascii_alphanumeric()
{
for &decomposed in &parts[..count] {
if !UnicodeBackend::is_mark(decomposed) {
out.push(decomposed);
}
}
} else {
out.push(c);
}
false
}
fn emit_folded(
token: &mut String,
canonical: &mut String,
needs_nfkc_again: bool,
policy: TokenizerPolicy,
unicode: &UnicodeBackend,
sink: &mut dyn FnMut(&str),
) {
let mut needs_nfkc_again = needs_nfkc_again;
while needs_nfkc_again {
unicode.normalize_into(token, canonical);
token.clear();
needs_nfkc_again = false;
for c in canonical.chars() {
if fold_into(c, token, policy, &mut needs_nfkc_again) {
emit_truncated(token, sink);
token.clear();
needs_nfkc_again = false;
}
}
if needs_nfkc_again && token == canonical {
needs_nfkc_again = false;
}
}
emit_truncated(token, sink);
}