use crate::data::characters::{CAT_PAGES, CAT_TABLE};
pub trait CharacterCategories {
fn is_letter(self) -> bool;
fn is_letter_cased(self) -> bool;
fn is_letter_uppercase(self) -> bool;
fn is_letter_lowercase(self) -> bool;
fn is_letter_titlecase(self) -> bool;
fn is_letter_modifier(self) -> bool;
fn is_letter_other(self) -> bool;
fn is_mark(self) -> bool;
fn is_mark_nonspacing(self) -> bool;
fn is_mark_spacing(self) -> bool;
fn is_mark_enclosing(self) -> bool;
fn is_letter_or_mark(self) -> bool;
fn is_number(self) -> bool;
fn is_number_decimal(self) -> bool;
fn is_number_letter(self) -> bool;
fn is_number_other(self) -> bool;
fn is_punctuation(self) -> bool;
fn is_punctuation_connector(self) -> bool;
fn is_punctuation_dash(self) -> bool;
fn is_punctuation_open(self) -> bool;
fn is_punctuation_close(self) -> bool;
fn is_punctuation_initial(self) -> bool;
fn is_punctuation_final(self) -> bool;
fn is_punctuation_other(self) -> bool;
fn is_symbol(self) -> bool;
fn is_symbol_math(self) -> bool;
fn is_symbol_currency(self) -> bool;
fn is_symbol_modifier(self) -> bool;
fn is_symbol_other(self) -> bool;
fn is_separator(self) -> bool;
fn is_separator_space(self) -> bool;
fn is_separator_line(self) -> bool;
fn is_separator_paragraph(self) -> bool;
fn is_other(self) -> bool;
fn is_control(self) -> bool;
fn is_format(self) -> bool;
fn is_private_use(self) -> bool;
fn is_unassigned(self) -> bool;
fn get_major_category(self) -> MajorCategory;
fn get_minor_category(self) -> MinorCategory;
}
#[derive(PartialEq, Debug)]
pub enum MinorCategory {
Lu,
Ll,
Lt,
Lm,
Lo,
Mn,
Mc,
Me,
Nd,
Nl,
No,
Pc,
Pd,
Ps,
Pe,
Pi,
Pf,
Po,
Sm,
Sk,
Sc,
So,
Zs,
Zl,
Zp,
Cc,
Cf,
Co,
Cn,
}
#[derive(PartialEq, Debug)]
pub enum MajorCategory {
L,
M,
N,
P,
S,
Z,
C,
}
struct Cat;
impl Cat {
#![allow(non_upper_case_globals)]
const Lu: u8 = 0x90;
const Ll: u8 = 0x91;
const Lt: u8 = 0x92;
const LC: u8 = 0x90;
const Lm: u8 = 0x83;
const Lo: u8 = 0x84;
const L: u8 = 0x80;
const Mn: u8 = 0x10;
const Mc: u8 = 0x11;
const Me: u8 = 0x12;
const M: u8 = 0x10;
const Nd: u8 = 0x20;
const Nl: u8 = 0x21;
const No: u8 = 0x22;
const N: u8 = 0x20;
const Pc: u8 = 0x30;
const Pd: u8 = 0x31;
const Ps: u8 = 0x32;
const Pe: u8 = 0x33;
const Pi: u8 = 0x34;
const Pf: u8 = 0x35;
const Po: u8 = 0x36;
const P: u8 = 0x30;
const Sm: u8 = 0x40;
const Sc: u8 = 0x41;
const Sk: u8 = 0x42;
const So: u8 = 0x43;
const S: u8 = 0x40;
const Zs: u8 = 0x50;
const Zl: u8 = 0x51;
const Zp: u8 = 0x52;
const Z: u8 = 0x50;
const Cc: u8 = 0x61;
const Cf: u8 = 0x62;
const Co: u8 = 0x64;
const Cn: u8 = 0x60;
const C: u8 = 0x60;
}
#[inline]
fn get_code(c: char) -> u8 {
CAT_PAGES[usize::from(CAT_TABLE[(c as usize) >> 8])][(c as usize) & 0xff]
}
impl CharacterCategories for char {
#[inline]
fn get_major_category(self) -> MajorCategory {
match get_code(self) & 0xf0 {
Cat::L => MajorCategory::L,
Cat::LC => MajorCategory::L,
Cat::M => MajorCategory::M,
Cat::N => MajorCategory::N,
Cat::P => MajorCategory::P,
Cat::S => MajorCategory::S,
Cat::Z => MajorCategory::Z,
Cat::C => MajorCategory::C,
_ => {
panic!("Corrupt character data")
}
}
}
#[inline]
fn get_minor_category(self) -> MinorCategory {
match get_code(self) {
Cat::Lu => MinorCategory::Lu,
Cat::Ll => MinorCategory::Ll,
Cat::Lt => MinorCategory::Lt,
Cat::Lm => MinorCategory::Lm,
Cat::Lo => MinorCategory::Lo,
Cat::Mn => MinorCategory::Mn,
Cat::Mc => MinorCategory::Mc,
Cat::Me => MinorCategory::Me,
Cat::Nd => MinorCategory::Nd,
Cat::Nl => MinorCategory::Nl,
Cat::No => MinorCategory::No,
Cat::Pc => MinorCategory::Pc,
Cat::Pd => MinorCategory::Pd,
Cat::Ps => MinorCategory::Ps,
Cat::Pe => MinorCategory::Pe,
Cat::Pi => MinorCategory::Pi,
Cat::Pf => MinorCategory::Pf,
Cat::Po => MinorCategory::Po,
Cat::Sm => MinorCategory::Sm,
Cat::Sc => MinorCategory::Sc,
Cat::Sk => MinorCategory::Sk,
Cat::So => MinorCategory::So,
Cat::Zs => MinorCategory::Zs,
Cat::Zl => MinorCategory::Zl,
Cat::Zp => MinorCategory::Zp,
Cat::Cc => MinorCategory::Cc,
Cat::Cf => MinorCategory::Cf,
Cat::Co => MinorCategory::Co,
Cat::Cn => MinorCategory::Cn,
_ => {
panic!("Corrupt character data")
}
}
}
#[inline]
fn is_letter(self) -> bool {
get_code(self) & Cat::L == Cat::L
}
#[inline]
fn is_letter_cased(self) -> bool {
get_code(self) & 0xf0 == Cat::LC
}
#[inline]
fn is_letter_uppercase(self) -> bool {
get_code(self) == Cat::Lu
}
#[inline]
fn is_letter_lowercase(self) -> bool {
get_code(self) == Cat::Ll
}
#[inline]
fn is_letter_titlecase(self) -> bool {
get_code(self) == Cat::Lt
}
#[inline]
fn is_letter_modifier(self) -> bool {
get_code(self) == Cat::Lm
}
#[inline]
fn is_letter_other(self) -> bool {
get_code(self) == Cat::Lo
}
#[inline]
fn is_mark(self) -> bool {
get_code(self) & 0xf0 == Cat::M
}
#[inline]
fn is_mark_nonspacing(self) -> bool {
get_code(self) == Cat::Mn
}
#[inline]
fn is_mark_spacing(self) -> bool {
get_code(self) == Cat::Mc
}
#[inline]
fn is_mark_enclosing(self) -> bool {
get_code(self) == Cat::Me
}
#[inline]
fn is_letter_or_mark(self) -> bool {
return get_code(self) & 0x60 == 0x00
}
#[inline]
fn is_number(self) -> bool {
get_code(self) & 0xf0 == Cat::N
}
#[inline]
fn is_number_decimal(self) -> bool {
get_code(self) == Cat::Nd
}
#[inline]
fn is_number_letter(self) -> bool {
get_code(self) == Cat::Nl
}
#[inline]
fn is_number_other(self) -> bool {
get_code(self) == Cat::No
}
#[inline]
fn is_punctuation(self) -> bool {
get_code(self) & 0xf0 == Cat::P
}
#[inline]
fn is_punctuation_connector(self) -> bool {
get_code(self) == Cat::Pc
}
#[inline]
fn is_punctuation_dash(self) -> bool {
get_code(self) == Cat::Pd
}
#[inline]
fn is_punctuation_open(self) -> bool {
get_code(self) == Cat::Ps
}
#[inline]
fn is_punctuation_close(self) -> bool {
get_code(self) == Cat::Pe
}
#[inline]
fn is_punctuation_initial(self) -> bool {
get_code(self) == Cat::Pi
}
#[inline]
fn is_punctuation_final(self) -> bool {
get_code(self) == Cat::Pf
}
#[inline]
fn is_punctuation_other(self) -> bool {
get_code(self) == Cat::Po
}
#[inline]
fn is_symbol(self) -> bool {
get_code(self) & 0xf0 == Cat::S
}
#[inline]
fn is_symbol_math(self) -> bool {
get_code(self) == Cat::Sm
}
#[inline]
fn is_symbol_currency(self) -> bool {
get_code(self) == Cat::Sc
}
#[inline]
fn is_symbol_modifier(self) -> bool {
get_code(self) == Cat::Sk
}
#[inline]
fn is_symbol_other(self) -> bool {
get_code(self) == Cat::So
}
#[inline]
fn is_separator(self) -> bool {
get_code(self) & 0xf0 == Cat::Z
}
#[inline]
fn is_separator_space(self) -> bool {
get_code(self) == Cat::Zs
}
#[inline]
fn is_separator_line(self) -> bool {
get_code(self) == Cat::Zl
}
#[inline]
fn is_separator_paragraph(self) -> bool {
get_code(self) == Cat::Zp
}
#[inline]
fn is_other(self) -> bool {
get_code(self) & 0xf0 == Cat::C
}
#[inline]
fn is_control(self) -> bool {
get_code(self) == Cat::Cc
}
#[inline]
fn is_format(self) -> bool {
get_code(self) == Cat::Cf
}
#[inline]
fn is_private_use(self) -> bool {
get_code(self) == Cat::Co
}
#[inline]
fn is_unassigned(self) -> bool {
get_code(self) == Cat::Cn
}
}
#[cfg(test)]
mod tests {
use crate::categories::*;
#[test]
fn character_categories() {
assert!('a'.is_letter());
assert!(!'a'.is_letter_uppercase());
assert!('Ü'.is_letter_uppercase());
assert!('Я'.is_letter_uppercase());
"𠜎 𠜱 𠝹 𠱓 𠱸 𠲖 𠳏 𠳕 𠴕 𠵼 𠵿 𠸎 𠸏 𠹷 𠺝 𠺢 𠻗 𠻹 𠻺 𠼭 𠼮 𠽌 𠾴 𠾼 𠿪 𡁜 𡁯 𡁵 𡁶 𡁻 𡃁 𡃉 𡇙 𢃇 𢞵 𢫕 𢭃 𢯊 𢱑 𢱕 𢳂 𢴈 𢵌 𢵧 𢺳 𣲷 𤓓 𤶸 𤷪 𥄫 𦉘 𦟌 𦧲 𦧺 𧨾 𨅝 𨈇 𨋢 𨳊 𨳍 𨳒 𩶘".chars()
.filter(|&c| c != ' ')
.for_each(
|c| {
assert!(c.is_letter());
assert!(c.is_letter_other());
assert!(!c.is_letter_cased());
}
);
assert!('\t'.is_control());
assert!(' '.is_separator_space());
assert!(
'['.is_punctuation_open(),
"Got character code of {}",
get_code('[')
);
assert!(']'.is_punctuation_close());
assert!('^'.is_symbol_modifier());
assert!('A'.is_letter_uppercase());
assert!('\u{00AD}'.is_format());
assert!('¾'.is_number_other());
assert!('«'.is_punctuation_initial());
assert!('»'.is_punctuation_final());
assert!('\u{0300}'.is_mark_nonspacing());
assert!('\u{0488}'.is_mark_enclosing());
assert!('٣'.is_number_decimal());
assert!('子'.is_letter_other());
assert!('ᾮ'.is_letter_titlecase());
assert!('\u{1B44}'.is_mark_spacing());
assert!('∈'.is_symbol_math());
assert!('‿'.is_punctuation_connector());
assert!('↹'.is_symbol_other());
assert!('⸗'.is_punctuation_dash());
assert!('Ⅷ'.is_number_letter());
assert!('\u{2028}'.is_separator_line());
assert!('\u{2029}'.is_separator_paragraph());
assert!('ゞ'.is_letter_modifier());
assert!('£'.is_symbol_currency());
assert!('\u{FFFF}'.is_unassigned());
assert!('\u{100000}'.is_private_use());
for c in "edä\u{0301}\u{20dd}\u{903}".chars() {
assert!(c.is_letter_or_mark());
}
for c in "45.$😀".chars() {
assert!(!c.is_letter_or_mark());
}
assert_eq!(']'.get_minor_category(), MinorCategory::Pe);
assert_eq!(']'.get_major_category(), MajorCategory::P);
}
}