1use unicode_normalization::UnicodeNormalization;
2
3pub fn normalize(text: &str) -> String {
4 text.nfkc().flat_map(char::to_lowercase).collect()
5}
6
7pub fn katakana_to_hiragana(text: &str) -> String {
8 text.chars()
9 .map(|ch| {
10 if ('ァ'..='ヶ').contains(&ch) {
11 char::from_u32(ch as u32 - 0x60).unwrap_or(ch)
12 } else {
13 ch
14 }
15 })
16 .collect()
17}
18
19pub fn hiragana_to_katakana(text: &str) -> String {
20 text.chars()
21 .map(|ch| {
22 if ('ぁ'..='ゖ').contains(&ch) {
23 char::from_u32(ch as u32 + 0x60).unwrap_or(ch)
24 } else {
25 ch
26 }
27 })
28 .collect()
29}
30
31pub fn contains_kana(text: &str) -> bool {
32 text.chars()
33 .any(|ch| ('ぁ'..='ゖ').contains(&ch) || ('ァ'..='ヶ').contains(&ch))
34}
35
36#[cfg(test)]
37mod tests {
38 use super::*;
39
40 #[test]
41 fn normalize_ascii_lowercase() {
42 assert_eq!(normalize("README.MD"), "readme.md");
43 }
44
45 #[test]
46 fn normalize_fullwidth_ascii_nfkc() {
47 assert_eq!(normalize("ABC123"), "abc123");
48 }
49
50 #[test]
51 fn normalize_halfwidth_katakana() {
52 assert_eq!(normalize("カメラ"), "カメラ");
53 }
54
55 #[test]
56 fn katakana_to_hiragana_basic() {
57 assert_eq!(katakana_to_hiragana("カメラ"), "かめら");
58 }
59
60 #[test]
61 fn hiragana_to_katakana_basic() {
62 assert_eq!(hiragana_to_katakana("しんじゅく"), "シンジュク");
63 }
64}