use crate::tables::translit::{DATA, KEYS, SPANS};
pub(crate) fn ascii_fold(c: char) -> Option<&'static str> {
let scalar = u32::from(c);
if scalar > 0xFFFF {
return None;
}
let index = KEYS.binary_search(&(scalar as u16)).ok()?;
let (offset, len) = SPANS[index];
let (offset, len) = (usize::from(offset), usize::from(len));
DATA.get(offset..offset + len)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn folds_what_unicode_decomposes() {
assert_eq!(ascii_fold('\u{E9}'), Some("e")); assert_eq!(ascii_fold('\u{C5}'), Some("A")); assert_eq!(ascii_fold('\u{FB01}'), Some("fi")); assert_eq!(ascii_fold('\u{FF21}'), Some("A")); assert_eq!(ascii_fold('\u{BD}'), Some("1/2")); assert_eq!(ascii_fold('\u{2075}'), Some("5")); }
#[test]
fn folds_what_it_does_not() {
assert_eq!(ascii_fold('\u{E6}'), Some("ae")); assert_eq!(ascii_fold('\u{153}'), Some("oe")); assert_eq!(ascii_fold('\u{DF}'), Some("ss")); assert_eq!(ascii_fold('\u{F8}'), Some("o")); assert_eq!(ascii_fold('\u{2014}'), Some("-")); assert_eq!(ascii_fold('\u{201C}'), Some("\"")); assert_eq!(ascii_fold('\u{2026}'), Some("...")); assert_eq!(ascii_fold('\u{20AC}'), Some("EUR")); assert_eq!(ascii_fold('\u{A9}'), Some("(C)")); }
#[test]
fn has_nothing_to_say_about_cjk_or_ascii() {
assert_eq!(ascii_fold('\u{4E00}'), None);
assert_eq!(ascii_fold('\u{3042}'), None);
assert_eq!(ascii_fold('\u{1F600}'), None);
assert_eq!(ascii_fold('a'), None);
}
#[test]
fn every_fold_is_ascii_and_the_keys_are_sorted() {
assert!(KEYS.windows(2).all(|w| w[0] < w[1]));
assert_eq!(KEYS.len(), SPANS.len());
for (i, &key) in KEYS.iter().enumerate() {
let (offset, len) = SPANS[i];
let text = DATA
.get(usize::from(offset)..usize::from(offset) + usize::from(len))
.unwrap_or_else(|| panic!("U+{key:04X} span is not a character boundary"));
assert!(!text.is_empty());
assert!(text.is_ascii(), "U+{key:04X} folds to {text:?}");
}
}
}