Skip to main content

skey_engine/
tables.rs

1//! Precomposed UTF-8 Vietnamese character lookup tables.
2//! tone 0=none, 1=grave, 2=acute, 3=hook, 4=tilde, 5=dot
3
4type ToneTable = [[&'static str; 6]; 3];
5
6pub static A_LO: ToneTable = [
7    ["a", "à", "á", "ả", "ã", "ạ"],
8    ["â", "ầ", "ấ", "ẩ", "ẫ", "ậ"],
9    ["ă", "ằ", "ắ", "ẳ", "ẵ", "ặ"],
10];
11pub static A_UP: ToneTable = [
12    ["A", "À", "Á", "Ả", "Ã", "Ạ"],
13    ["Â", "Ầ", "Ấ", "Ẩ", "Ẫ", "Ậ"],
14    ["Ă", "Ằ", "Ắ", "Ẳ", "Ẵ", "Ặ"],
15];
16pub static E_LO: ToneTable = [
17    ["e", "è", "é", "ẻ", "ẽ", "ẹ"],
18    ["ê", "ề", "ế", "ể", "ễ", "ệ"],
19    ["e", "è", "é", "ẻ", "ẽ", "ẹ"],
20];
21pub static E_UP: ToneTable = [
22    ["E", "È", "É", "Ẻ", "Ẽ", "Ẹ"],
23    ["Ê", "Ề", "Ế", "Ể", "Ễ", "Ệ"],
24    ["E", "È", "É", "Ẻ", "Ẽ", "Ẹ"],
25];
26pub static I_LO: ToneTable = [
27    ["i", "ì", "í", "ỉ", "ĩ", "ị"],
28    ["i", "ì", "í", "ỉ", "ĩ", "ị"],
29    ["i", "ì", "í", "ỉ", "ĩ", "ị"],
30];
31pub static I_UP: ToneTable = [
32    ["I", "Ì", "Í", "Ỉ", "Ĩ", "Ị"],
33    ["I", "Ì", "Í", "Ỉ", "Ĩ", "Ị"],
34    ["I", "Ì", "Í", "Ỉ", "Ĩ", "Ị"],
35];
36pub static O_LO: ToneTable = [
37    ["o", "ò", "ó", "ỏ", "õ", "ọ"],
38    ["ô", "ồ", "ố", "ổ", "ỗ", "ộ"],
39    ["ơ", "ờ", "ớ", "ở", "ỡ", "ợ"],
40];
41pub static O_UP: ToneTable = [
42    ["O", "Ò", "Ó", "Ỏ", "Õ", "Ọ"],
43    ["Ô", "Ồ", "Ố", "Ổ", "Ỗ", "Ộ"],
44    ["Ơ", "Ờ", "Ớ", "Ở", "Ỡ", "Ợ"],
45];
46pub static U_LO: ToneTable = [
47    ["u", "ù", "ú", "ủ", "ũ", "ụ"],
48    ["u", "ù", "ú", "ủ", "ũ", "ụ"],
49    ["ư", "ừ", "ứ", "ử", "ữ", "ự"],
50];
51pub static U_UP: ToneTable = [
52    ["U", "Ù", "Ú", "Ủ", "Ũ", "Ụ"],
53    ["U", "Ù", "Ú", "Ủ", "Ũ", "Ụ"],
54    ["Ư", "Ừ", "Ứ", "Ử", "Ữ", "Ự"],
55];
56pub static Y_LO: ToneTable = [
57    ["y", "ỳ", "ý", "ỷ", "ỹ", "ỵ"],
58    ["y", "ỳ", "ý", "ỷ", "ỹ", "ỵ"],
59    ["y", "ỳ", "ý", "ỷ", "ỹ", "ỵ"],
60];
61pub static Y_UP: ToneTable = [
62    ["Y", "Ỳ", "Ý", "Ỷ", "Ỹ", "Ỵ"],
63    ["Y", "Ỳ", "Ý", "Ỷ", "Ỹ", "Ỵ"],
64    ["Y", "Ỳ", "Ý", "Ỷ", "Ỹ", "Ỵ"],
65];
66
67pub fn vowel_utf8(base: char, variant: u8, tone: u8, upper: bool) -> &'static str {
68    let v = (variant as usize).min(2);
69    let t = (tone as usize).min(5);
70    let table: &ToneTable = match base.to_ascii_lowercase() {
71        'a' => if upper { &A_UP } else { &A_LO },
72        'e' => if upper { &E_UP } else { &E_LO },
73        'i' => if upper { &I_UP } else { &I_LO },
74        'o' => if upper { &O_UP } else { &O_LO },
75        'u' => if upper { &U_UP } else { &U_LO },
76        'y' => if upper { &Y_UP } else { &Y_LO },
77        _ => return if upper { &A_UP } else { &A_LO }[0][0], // fallback
78    };
79    table[v][t]
80}
81
82pub fn is_vowel(c: char) -> bool {
83    matches!(c.to_ascii_lowercase(), 'a' | 'e' | 'i' | 'o' | 'u' | 'y')
84}
85
86pub const D_STROKE_LO: &str = "\u{0111}"; // đ
87pub const D_STROKE_UP: &str = "\u{0110}"; // Đ
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn basic_vowels() {
95        assert_eq!(vowel_utf8('a', 0, 2, false), "á");
96        assert_eq!(vowel_utf8('a', 0, 1, false), "à");
97        assert_eq!(vowel_utf8('a', 1, 2, false), "ấ");
98        assert_eq!(vowel_utf8('a', 2, 2, false), "ắ");
99        assert_eq!(vowel_utf8('o', 2, 1, false), "ờ");
100        assert_eq!(vowel_utf8('u', 2, 2, false), "ứ");
101        assert_eq!(vowel_utf8('e', 1, 2, false), "ế");
102    }
103
104    #[test]
105    fn uppercase_vowels() {
106        assert_eq!(vowel_utf8('a', 0, 2, true), "Á");
107        assert_eq!(vowel_utf8('A', 1, 0, true), "Â");
108        assert_eq!(vowel_utf8('o', 2, 1, true), "Ờ");
109    }
110}