use std::fmt::{Debug, Display};
use std::hash::Hash;
pub trait PhoneticUnit:
Copy + Clone + Eq + PartialEq + Ord + Hash + Debug + Display + Send + Sync + 'static
{
const DISPLAY_WIDTH: usize;
fn from_str(s: &str) -> Vec<Self>;
fn try_from_str(s: &str) -> Option<Vec<Self>>;
fn units_to_string(units: &[Self]) -> String;
fn to_lowercase(unit: Self) -> Self;
fn is_vowel(unit: Self) -> bool;
fn is_alphabetic(unit: Self) -> bool;
fn from_char(c: char) -> Option<Self>;
fn to_char(unit: Self) -> char;
fn format_for_display(unit: Self) -> String {
Self::to_char(unit).to_string()
}
}
impl PhoneticUnit for u8 {
const DISPLAY_WIDTH: usize = 1;
#[inline]
fn from_str(s: &str) -> Vec<Self> {
s.bytes().collect()
}
#[inline]
fn try_from_str(s: &str) -> Option<Vec<Self>> {
if s.is_ascii() {
Some(s.bytes().collect())
} else {
None
}
}
#[inline]
fn units_to_string(units: &[Self]) -> String {
String::from_utf8_lossy(units).into_owned()
}
#[inline]
fn to_lowercase(unit: Self) -> Self {
unit.to_ascii_lowercase()
}
#[inline]
fn is_vowel(unit: Self) -> bool {
matches!(
unit,
b'a' | b'e' | b'i' | b'o' | b'u' | b'A' | b'E' | b'I' | b'O' | b'U'
)
}
#[inline]
fn is_alphabetic(unit: Self) -> bool {
unit.is_ascii_alphabetic()
}
#[inline]
fn from_char(c: char) -> Option<Self> {
if c.is_ascii() {
Some(c as u8)
} else {
None
}
}
#[inline]
fn to_char(unit: Self) -> char {
unit as char
}
fn format_for_display(unit: Self) -> String {
if unit.is_ascii_graphic() || unit == b' ' {
(unit as char).to_string()
} else {
format!("0x{:02X}", unit)
}
}
}
impl PhoneticUnit for char {
const DISPLAY_WIDTH: usize = 1;
#[inline]
fn from_str(s: &str) -> Vec<Self> {
s.chars().collect()
}
#[inline]
fn try_from_str(s: &str) -> Option<Vec<Self>> {
Some(s.chars().collect())
}
#[inline]
fn units_to_string(units: &[Self]) -> String {
units.iter().collect()
}
#[inline]
fn to_lowercase(unit: Self) -> Self {
unit.to_lowercase().next().unwrap_or(unit)
}
#[inline]
fn is_vowel(unit: Self) -> bool {
matches!(
unit,
'a' | 'e' | 'i' | 'o' | 'u' | 'A' | 'E' | 'I' | 'O' | 'U'
)
}
#[inline]
fn is_alphabetic(unit: Self) -> bool {
unit.is_alphabetic()
}
#[inline]
fn from_char(c: char) -> Option<Self> {
Some(c)
}
#[inline]
fn to_char(unit: Self) -> char {
unit
}
}
#[inline]
pub fn is_consonant<U: PhoneticUnit>(unit: U) -> bool {
U::is_alphabetic(unit) && !U::is_vowel(unit)
}
pub fn to_lowercase_vec<U: PhoneticUnit>(units: &[U]) -> Vec<U> {
units.iter().map(|&u| U::to_lowercase(u)).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_u8_from_str() {
let units: Vec<u8> = PhoneticUnit::from_str("hello");
assert_eq!(units, vec![b'h', b'e', b'l', b'l', b'o']);
}
#[test]
fn test_u8_try_from_str_ascii() {
let units: Option<Vec<u8>> = PhoneticUnit::try_from_str("hello");
assert_eq!(units, Some(vec![b'h', b'e', b'l', b'l', b'o']));
}
#[test]
fn test_u8_try_from_str_non_ascii() {
let units: Option<Vec<u8>> = PhoneticUnit::try_from_str("héllo");
assert_eq!(units, None);
}
#[test]
fn test_u8_units_to_string() {
let s = u8::units_to_string(&[b'h', b'e', b'l', b'l', b'o']);
assert_eq!(s, "hello");
}
#[test]
fn test_u8_to_lowercase() {
assert_eq!(u8::to_lowercase(b'A'), b'a');
assert_eq!(u8::to_lowercase(b'Z'), b'z');
assert_eq!(u8::to_lowercase(b'a'), b'a');
assert_eq!(u8::to_lowercase(b'5'), b'5');
}
#[test]
fn test_u8_is_vowel() {
assert!(u8::is_vowel(b'a'));
assert!(u8::is_vowel(b'e'));
assert!(u8::is_vowel(b'i'));
assert!(u8::is_vowel(b'o'));
assert!(u8::is_vowel(b'u'));
assert!(u8::is_vowel(b'A'));
assert!(u8::is_vowel(b'E'));
assert!(!u8::is_vowel(b'b'));
assert!(!u8::is_vowel(b'z'));
}
#[test]
fn test_u8_from_char() {
assert_eq!(u8::from_char('a'), Some(b'a'));
assert_eq!(u8::from_char('Z'), Some(b'Z'));
assert_eq!(u8::from_char('é'), None); }
#[test]
fn test_u8_to_char() {
assert_eq!(u8::to_char(b'a'), 'a');
assert_eq!(u8::to_char(b'Z'), 'Z');
}
#[test]
fn test_u8_format_for_display() {
assert_eq!(u8::format_for_display(b'a'), "a");
assert_eq!(u8::format_for_display(b' '), " ");
assert_eq!(u8::format_for_display(0x00), "0x00");
assert_eq!(u8::format_for_display(0x7F), "0x7F");
}
#[test]
fn test_char_from_str() {
let units: Vec<char> = PhoneticUnit::from_str("hello");
assert_eq!(units, vec!['h', 'e', 'l', 'l', 'o']);
}
#[test]
fn test_char_from_str_unicode() {
let units: Vec<char> = PhoneticUnit::from_str("héllo");
assert_eq!(units, vec!['h', 'é', 'l', 'l', 'o']);
}
#[test]
fn test_char_try_from_str() {
let units: Option<Vec<char>> = PhoneticUnit::try_from_str("héllo");
assert_eq!(units, Some(vec!['h', 'é', 'l', 'l', 'o']));
}
#[test]
fn test_char_units_to_string() {
let s = char::units_to_string(&['h', 'é', 'l', 'l', 'o']);
assert_eq!(s, "héllo");
}
#[test]
fn test_char_to_lowercase() {
assert_eq!(<char as PhoneticUnit>::to_lowercase('A'), 'a');
assert_eq!(<char as PhoneticUnit>::to_lowercase('Z'), 'z');
assert_eq!(<char as PhoneticUnit>::to_lowercase('É'), 'é');
assert_eq!(<char as PhoneticUnit>::to_lowercase('a'), 'a');
}
#[test]
fn test_char_is_vowel() {
assert!(char::is_vowel('a'));
assert!(char::is_vowel('e'));
assert!(char::is_vowel('A'));
assert!(!char::is_vowel('b'));
assert!(!char::is_vowel('é')); }
#[test]
fn test_char_from_char() {
assert_eq!(char::from_char('a'), Some('a'));
assert_eq!(char::from_char('é'), Some('é'));
}
#[test]
fn test_char_to_char() {
assert_eq!(char::to_char('a'), 'a');
assert_eq!(char::to_char('é'), 'é');
}
#[test]
fn test_is_consonant_u8() {
assert!(is_consonant(b'b'));
assert!(is_consonant(b'k'));
assert!(!is_consonant(b'a'));
assert!(!is_consonant(b'5'));
}
#[test]
fn test_is_consonant_char() {
assert!(is_consonant('b'));
assert!(is_consonant('k'));
assert!(!is_consonant('a'));
assert!(!is_consonant('5'));
}
#[test]
fn test_to_lowercase_vec_u8() {
let input: Vec<u8> = vec![b'H', b'E', b'L', b'L', b'O'];
let result = to_lowercase_vec(&input);
assert_eq!(result, vec![b'h', b'e', b'l', b'l', b'o']);
}
#[test]
fn test_to_lowercase_vec_char() {
let input: Vec<char> = vec!['H', 'É', 'L', 'L', 'O'];
let result = to_lowercase_vec(&input);
assert_eq!(result, vec!['h', 'é', 'l', 'l', 'o']);
}
}