fast-unicode-general-category 1.0.0

Work with Unicode General Category.
Documentation
#![allow(unused_assignments)]

use bytes::{Bytes, Buf};
use num_enum::TryFromPrimitive;

static BASIC_PLANE: &'static [u8] = include_bytes!("./data/basic.bin");
static SUPPLEMENTARY_PLANE: &'static [u8] = include_bytes!("./data/supplementary.bin");

#[derive(Copy, Clone, PartialEq, TryFromPrimitive)]
#[repr(u8)]
pub enum GeneralCategory {
    UppercaseLetter = 0,
    LowercaseLetter = 1,
    TitlecaseLetter = 2,
    ModifierLetter = 3,
    OtherLetter = 4,
    NonspacingMark = 5,
    SpacingCombiningMark = 6,
    EnclosingMark = 7,
    DecimalDigitNumber = 8,
    LetterNumber = 9,
    OtherNumber = 10,
    ConnectorPunctuation = 11,
    DashPunctuation = 12,
    OpenPunctuation = 13,
    ClosePunctuation = 14,
    InitialQuotePunctuation = 15,
    FinalQuotePunctuation = 16,
    OtherPunctuation = 17,
    MathSymbol = 18,
    CurrencySymbol = 19,
    ModifierSymbol = 20,
    OtherSymbol = 21,
    SpaceSeparator = 22,
    LineSeparator = 23,
    ParagraphSeparator = 24,
    ControlOther = 25,
    FormatOther = 26,
    SurrogateOther = 27,
    PrivateUseOther = 28,
    NotAssignedOther = 29,
}

impl GeneralCategory {
    pub fn is_letter(self) -> bool {
        self as u8 <= 4
    }
    pub fn is_mark(self) -> bool {
        self as u8 >= 5 && self as u8 <= 7
    }
    pub fn is_number(self) -> bool {
        self as u8 >= 8 && self as u8 <= 10
    }
    pub fn is_punctuation(self) -> bool {
        self as u8 >= 11 && self as u8 <= 17
    }
    pub fn is_symbol(self) -> bool {
        self as u8 >= 18 && self as u8 <= 21
    }
    pub fn is_separator(self) -> bool {
        self as u8 >= 22 && self as u8 <= 24
    }
    pub fn is_other(self) -> bool {
        self as u8 >= 25 && self as u8 <= 29
    }
}

impl<'a> TryFrom<&'a str> for GeneralCategory {
    type Error = ();
    fn try_from(value: &'a str) -> Result<Self, Self::Error> {
        use GeneralCategory as C;
        (match value {
            "Lu" => Ok(C::UppercaseLetter),
            "Ll" => Ok(C::LowercaseLetter),
            "Lt" => Ok(C::TitlecaseLetter),
            "Lm" => Ok(C::ModifierLetter),
            "Lo" => Ok(C::OtherLetter),
            "Mn" => Ok(C::NonspacingMark),
            "Mc" => Ok(C::SpacingCombiningMark),
            "Me" => Ok(C::EnclosingMark),
            "Nd" => Ok(C::DecimalDigitNumber),
            "Nl" => Ok(C::LetterNumber),
            "No" => Ok(C::OtherNumber),
            "Pc" => Ok(C::ConnectorPunctuation),
            "Pd" => Ok(C::DashPunctuation),
            "Ps" => Ok(C::OpenPunctuation),
            "Pe" => Ok(C::ClosePunctuation),
            "Pi" => Ok(C::InitialQuotePunctuation),
            "Pf" => Ok(C::FinalQuotePunctuation),
            "Po" => Ok(C::OtherPunctuation),
            "Sm" => Ok(C::MathSymbol),
            "Sc" => Ok(C::CurrencySymbol),
            "Sk" => Ok(C::ModifierSymbol),
            "So" => Ok(C::OtherSymbol),
            "Zs" => Ok(C::SpaceSeparator),
            "Zl" => Ok(C::LineSeparator),
            "Zp" => Ok(C::ParagraphSeparator),
            "Cc" => Ok(C::ControlOther),
            "Cf" => Ok(C::FormatOther),
            "Cs" => Ok(C::SurrogateOther),
            "Co" => Ok(C::PrivateUseOther),
            "Cn" => Ok(C::NotAssignedOther),
            _ => Err(()),
        }).to_owned()
    }
}

impl ToString for GeneralCategory {
    fn to_string(&self) -> String {
        use GeneralCategory as C;
        (match self {
            C::UppercaseLetter => "Lu",
            C::LowercaseLetter => "Ll",
            C::TitlecaseLetter => "Lt",
            C::ModifierLetter => "Lm",
            C::OtherLetter => "Lo",
            C::NonspacingMark => "Mn",
            C::SpacingCombiningMark => "Mc",
            C::EnclosingMark => "Me",
            C::DecimalDigitNumber => "Nd",
            C::LetterNumber => "Nl",
            C::OtherNumber => "No",
            C::ConnectorPunctuation => "Pc",
            C::DashPunctuation => "Pd",
            C::OpenPunctuation => "Ps",
            C::ClosePunctuation => "Pe",
            C::InitialQuotePunctuation => "Pi",
            C::FinalQuotePunctuation => "Pf",
            C::OtherPunctuation => "Po",
            C::MathSymbol => "Sm",
            C::CurrencySymbol => "Sc",
            C::ModifierSymbol => "Sk",
            C::OtherSymbol => "So",
            C::SpaceSeparator => "Zs",
            C::LineSeparator => "Zl",
            C::ParagraphSeparator => "Zp",
            C::ControlOther => "Cc",
            C::FormatOther => "Cf",
            C::SurrogateOther => "Cs",
            C::PrivateUseOther => "Co",
            C::NotAssignedOther => "Cn",
        }).to_owned()
    }
}

impl From<char> for GeneralCategory {
    fn from(code_point: char) -> Self {
        let code_point = code_point as usize;
        let mut compare_code_point: usize = 0;
        let mut count: usize = 0;
        let mut category_value: u8 = 0;
        if code_point < 0x10000 {
            let mut plane = Bytes::from_static(BASIC_PLANE);
            loop {
                if !plane.has_remaining() {
                    break;
                }
                category_value = plane.get_u8();
                count = plane.get_u16_le() as usize;
                compare_code_point += count;
                if code_point < compare_code_point {
                    return GeneralCategory::try_from(category_value).unwrap();
                }
            }
        } else {
            let mut plane = Bytes::from_static(SUPPLEMENTARY_PLANE);
            loop {
                if !plane.has_remaining() {
                    break;
                }
                category_value = plane.get_u8();
                count = bytes_get_u24_le(&mut plane);
                compare_code_point += count;
                if code_point < compare_code_point {
                    return GeneralCategory::try_from(category_value).unwrap();
                }
            }
        }
        GeneralCategory::NotAssignedOther
    }
}

fn bytes_get_u24_le(bytes: &mut Bytes) -> usize {
    (bytes.get_u16_le() as usize) | ((bytes.get_u8() as usize) << 16)
}