#[inline(always)]
pub const fn is_vowel_fullwidth(c: char) -> bool {
matches!(
c,
'A' | 'E' | 'I' | 'O' | 'U' | 'Y' | 'a' | 'e' | 'i' | 'o' | 'u' | 'y'
)
}
#[inline(always)]
pub const fn is_aeiou_fullwidth(c: char) -> bool {
matches!(
c,
'A' | 'E' | 'I' | 'O' | 'U' | 'a' | 'e' | 'i' | 'o' | 'u'
)
}
#[inline(always)]
pub const fn is_consonant_fullwidth(c: char) -> bool {
matches!(c, 'A'..='Z' | 'a'..='z') && !is_vowel_fullwidth(c)
}
#[rustfmt::skip]
#[inline(always)]
pub const fn is_continuant_fullwidth(c: char) -> bool {
matches!(
c,
'S' | 'Z' | 'F' | 'V' | 'H' | 'M' | 'L' | 'R' | 'W'
| 's' | 'z' | 'f' | 'v' | 'h' | 'm' | 'l' | 'r' | 'w'
)
}
#[rustfmt::skip]
#[inline(always)]
fn is_allowed_nucleus2(a: char, b: char) -> bool {
matches!(
(a, b),
('a', 'i') | ('a', 'y') | ('a', 'u') | ('a', 'w') | ('e', 'a') |
('e', 'e') | ('e', 'i') | ('e', 'y') | ('e', 'u') | ('i', 'e') |
('o', 'a') | ('o', 'i') | ('o', 'o') | ('o', 'u') | ('o', 'w') |
('o', 'y') | ('u', 'e') | ('u', 'y')
)
}
#[rustfmt::skip]
#[inline(always)]
fn is_allowed_onset2(a: char, b: char) -> bool {
matches!(
(a, b),
('b', 'l') | ('b', 'r') | ('c', 'l') | ('c', 'r') | ('d', 'r') |
('f', 'l') | ('f', 'r') | ('g', 'l') | ('g', 'r') | ('p', 'l') |
('p', 'r') | ('s', 'c') | ('s', 'k') | ('s', 'l') | ('s', 'm') |
('s', 'n') | ('s', 'p') | ('s', 't') | ('s', 'w') | ('t', 'r') |
('t', 'w') | ('s', 'h') | ('c', 'h') | ('t', 'h') | ('w', 'h') |
('p', 'h') | ('w', 'r') | ('k', 'n') | ('q', 'u')
)
}
#[rustfmt::skip]
#[inline(always)]
fn is_allowed_rime2(a: char, b: char) -> bool {
matches!(
(a, b),
('c', 'k') | ('c', 't') | ('f', 't') | ('l', 'd') | ('l', 'f') |
('l', 'k') | ('l', 'm') | ('l', 'n') | ('l', 'p') | ('l', 't') |
('m', 'p') | ('n', 'd') | ('n', 'g') | ('n', 'k') | ('n', 't') |
('p', 't') | ('r', 'b') | ('r', 'd') | ('r', 'f') | ('r', 'g') |
('r', 'k') | ('r', 'l') | ('r', 'm') | ('r', 'n') | ('r', 'p') |
('r', 't') | ('s', 'k') | ('w', 'l')
)
}
#[rustfmt::skip]
#[inline(always)]
pub fn is_pronounceable(chars: [char; 3]) -> bool {
let c0 = fullwidth_lower(chars[0]);
let c1 = fullwidth_lower(chars[1]);
let c2 = fullwidth_lower(chars[2]);
let is_v0 = is_aeiou_fullwidth(c0);
let is_v1 = is_vowel_fullwidth(c1);
let is_v2 = is_vowel_fullwidth(c2);
match (is_v0, is_v1, is_v2) {
(false, false, false) => false, (true, false, false) => {
(
c1 == c2 &&
matches!(c1,
'b' | 'd' | 'f' | 'g' | 'h' | 'l' | 'm' | 'n' | 'p' | 'r' | 's'
)
) || is_allowed_rime2(c1, c2) }
(false, true, false) => true, (false, false, true) => is_allowed_onset2(c0, c1), (true, true, false) => is_allowed_nucleus2(c0, c1), (false, true, true) => is_allowed_nucleus2(c1, c2), (true, false, true) => true, (true, true, true) => false, }
}
#[inline(always)]
const fn fullwidth_lower(c: char) -> char {
match c {
'A'..='Z' => char::from_u32(c as u32 + 0x20).unwrap(),
_ => c,
}
}
#[inline(always)]
pub fn fullwidth_to_halfwidth(chars: Vec<char>) -> String {
chars
.into_iter()
.map(|c| match c {
'\u{FF21}'..='\u{FF3A}' | '\u{FF41}'..='\u{FF5A}' => {
char::from_u32(c as u32 - 0xFEE0).unwrap()
}
_ => c,
})
.collect()
}
pub fn to_halfwidth_lower_string(s: &str) -> String {
s.chars()
.map(|c| match c {
'A'..='Z' => char::from_u32(c as u32 - 0xFEE0 + 0x20).unwrap(),
'a'..='z' => char::from_u32(c as u32 - 0xFEE0).unwrap(),
'A'..='Z' => c.to_ascii_lowercase(),
'0'..='9' => char::from_u32(c as u32 - 0xFEE0).unwrap(),
_ => c,
})
.collect()
}