use crate::char::{Char, CharPool};
use crate::transliterator::{TransliterationError, Transliterator};
use std::borrow::Cow;
pub struct SimpleTransliterator {
mappings: &'static phf::Map<&'static str, &'static str>,
}
impl SimpleTransliterator {
pub fn new(mappings: &'static phf::Map<&'static str, &'static str>) -> Self {
Self { mappings }
}
}
impl Transliterator for SimpleTransliterator {
fn transliterate<'a, 'b>(
&self,
pool: &mut CharPool<'a, 'b>,
input: &[&'a Char<'a, 'b>],
) -> Result<Vec<&'a Char<'a, 'b>>, TransliterationError> {
let mut result = Vec::new();
let mut offset = 0;
for char in input {
if char.c.is_empty() {
result.push(*char);
continue;
}
let nc = if let Some(mapped) = self.mappings.get(char.c.as_ref()) {
pool.new_char_from(Cow::Borrowed(mapped), offset, char)
} else {
pool.new_with_offset(char, offset)
};
offset += nc.c.len();
result.push(nc);
}
Ok(result)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::char::from_chars;
fn create_basic_test_transliterator() -> SimpleTransliterator {
static BASIC_MAPPINGS: phf::Map<&'static str, &'static str> = phf::phf_map! {
"a" => "α",
"b" => "β",
"c" => "γ",
"h" => "こんにちは",
};
SimpleTransliterator::new(&BASIC_MAPPINGS)
}
fn create_unicode_test_transliterator() -> SimpleTransliterator {
static UNICODE_MAPPINGS: phf::Map<&'static str, &'static str> = phf::phf_map! {
"α" => "a",
"β" => "b",
"こ" => "ko",
"ん" => "n",
"ば" => "ba",
"は" => "wa",
};
SimpleTransliterator::new(&UNICODE_MAPPINGS)
}
#[test]
fn test_simple_transliterator_basic_mappings() {
let transliterator = create_basic_test_transliterator();
let test_cases = &[
("a", "α"),
("b", "β"),
("c", "γ"),
("ab", "αβ"),
("abc", "αβγ"),
("h", "こんにちは"),
];
for (input, expected) in test_cases {
let mut pool = CharPool::new();
let input_chars = pool.build_char_array(input);
let result = transliterator
.transliterate(&mut pool, &input_chars)
.unwrap();
let result_string = from_chars(result.iter().cloned());
assert_eq!(
result_string, *expected,
"Failed for input '{input}', result: '{result_string}'"
);
}
}
#[test]
fn test_simple_transliterator_no_mapping() {
let transliterator = create_basic_test_transliterator();
let test_cases = &[
("d", "d"),
("x", "x"),
("xyz", "xyz"),
("1", "1"),
("!", "!"),
(" ", " "),
];
for (input, expected) in test_cases {
let mut pool = CharPool::new();
let input_chars = pool.build_char_array(input);
let result = transliterator
.transliterate(&mut pool, &input_chars)
.unwrap();
let result_string = from_chars(result.iter().filter(|c| !c.c.is_empty()).copied());
assert_eq!(result_string, *expected, "Failed for input '{input}'");
}
}
#[test]
fn test_simple_transliterator_mixed_input() {
let transliterator = create_basic_test_transliterator();
let test_cases = &[
("ax", "αx"),
("bz", "βz"),
("h world", "こんにちは world"),
("a1b2c3", "α1β2γ3"),
("test_a_test", "test_α_test"),
];
for (input, expected) in test_cases {
let mut pool = CharPool::new();
let input_chars = pool.build_char_array(input);
let result = transliterator
.transliterate(&mut pool, &input_chars)
.unwrap();
let result_string = from_chars(result.iter().filter(|c| !c.c.is_empty()).copied());
assert_eq!(result_string, *expected, "Failed for input '{input}'");
}
}
#[test]
fn test_simple_transliterator_unicode_input() {
let transliterator = create_unicode_test_transliterator();
let test_cases = &[
("α", "a"),
("β", "b"),
("こ", "ko"),
("ん", "n"),
("こんばんは", "konbanwa"),
("αβγ", "abγ"), ];
for (input, expected) in test_cases {
let mut pool = CharPool::new();
let input_chars = pool.build_char_array(input);
let result = transliterator
.transliterate(&mut pool, &input_chars)
.unwrap();
let result_string = from_chars(result.iter().filter(|c| !c.c.is_empty()).copied());
assert_eq!(result_string, *expected, "Failed for input '{input}'");
}
}
#[test]
fn test_simple_transliterator_empty_input() {
let transliterator = create_basic_test_transliterator();
let mut pool = CharPool::new();
let input_chars = pool.build_char_array("");
let result = transliterator
.transliterate(&mut pool, &input_chars)
.unwrap();
let non_sentinel_chars: Vec<_> = result.iter().filter(|c| !c.c.is_empty()).collect();
assert!(
non_sentinel_chars.is_empty(),
"Expected empty result for empty input"
);
}
#[test]
fn test_simple_transliterator_sentinel_handling() {
let transliterator = create_basic_test_transliterator();
let mut pool = CharPool::new();
let sentinel = pool.new_sentinel(0);
let input = vec![sentinel];
let result = transliterator.transliterate(&mut pool, &input).unwrap();
assert_eq!(result.len(), 1);
assert!(
result[0].c.is_empty(),
"Sentinel character should be preserved"
);
}
#[test]
fn test_simple_transliterator_direct_usage() {
let transliterator = create_basic_test_transliterator();
let mut pool = CharPool::new();
let input_chars = pool.build_char_array("abc");
let result = transliterator.transliterate(&mut pool, &input_chars);
assert!(result.is_ok());
let result_chars = result.unwrap();
let result_string = from_chars(result_chars.iter().cloned());
assert_eq!(result_string, "αβγ");
}
#[test]
fn test_simple_transliterator_character_offsets() {
let transliterator = create_basic_test_transliterator();
let mut pool = CharPool::new();
let input_chars = pool.build_char_array("abc");
let result = transliterator.transliterate(&mut pool, &input_chars);
assert!(result.is_ok());
let result_chars = result.unwrap();
assert_eq!(result_chars[0].offset, 0); assert_eq!(result_chars[1].offset, 2); assert_eq!(result_chars[2].offset, 4);
assert!(result_chars[0].source.is_some());
assert!(result_chars[1].source.is_some());
assert!(result_chars[2].source.is_some());
}
#[test]
fn test_simple_transliterator_variation_selectors() {
let transliterator = create_basic_test_transliterator();
let input_with_vs = "a\u{FE00}b"; let mut pool = CharPool::new();
let input_chars = pool.build_char_array(input_with_vs);
let result = transliterator.transliterate(&mut pool, &input_chars);
assert!(result.is_ok());
let result_chars = result.unwrap();
let result_string = from_chars(result_chars.iter().cloned());
assert!(result_string.contains("β"));
}
#[test]
fn test_simple_transliterator_multibyte_unicode() {
let transliterator = create_basic_test_transliterator();
let input = "a🎌b"; let mut pool = CharPool::new();
let input_chars = pool.build_char_array(input);
let result = transliterator.transliterate(&mut pool, &input_chars);
assert!(result.is_ok());
let result_chars = result.unwrap();
let result_string = from_chars(result_chars.iter().cloned());
assert_eq!(result_string, "α🎌β");
}
}