pub use super::compose::Decompose;
#[doc(inline)]
pub use super::unicode_data::{
BidiClass, Block, Category, ClusterBreak, JoiningType, LineBreak, Script, WordBreak,
UNICODE_VERSION,
};
use super::compose::{compose_pair, decompose, decompose_compat};
use super::unicode_data::{
get_record_index, MyanmarClass, Record, UseClass, BRACKETS, MIRRORS, RECORDS, SCRIPTS_BY_TAG,
SCRIPT_COMPLEXITY, SCRIPT_NAMES, SCRIPT_TAGS,
};
use crate::Tag;
use core::char::from_u32_unchecked;
const RECORD_MASK: u16 = 0x1FFF;
const BOUNDARY_SHIFT: u16 = 13;
#[derive(Copy, Clone, PartialEq, Eq, Default)]
pub struct Properties(u16);
impl Properties {
fn new(ch: u32) -> Self {
Self(get_record_index(ch as usize) as u16)
}
pub fn category(self) -> Category {
self.record().category
}
pub fn block(self) -> Block {
self.record().block
}
pub fn script(self) -> Script {
self.record().script
}
pub fn combining_class(self) -> u8 {
self.record().combining_class
}
pub fn bidi_class(self) -> BidiClass {
self.record().bidi_class
}
pub fn joining_type(self) -> JoiningType {
self.record().joining_type
}
pub fn cluster_break(self) -> ClusterBreak {
self.record().cluster_break
}
pub fn word_break(self) -> WordBreak {
self.record().word_break
}
pub fn line_break(self) -> LineBreak {
self.record().line_break
}
pub fn is_emoji(self) -> bool {
self.record().flags.is_emoji()
}
pub fn is_extended_pictographic(self) -> bool {
self.record().flags.is_extended_pictographic()
}
pub fn is_open_bracket(self) -> bool {
self.record().flags.is_open_bracket()
}
pub fn is_close_bracket(self) -> bool {
self.record().flags.is_close_bracket()
}
pub(crate) fn is_ignorable(self) -> bool {
self.record().flags.is_ignorable()
}
pub(crate) fn is_variation_selector(self) -> bool {
self.record().flags.is_variation_selector()
}
pub(crate) fn contributes_to_shaping(self) -> bool {
self.record().flags.contributes_to_shaping()
}
pub(crate) fn with_boundary(mut self, b: u16) -> Self {
self.set_boundary(b);
self
}
pub(crate) fn boundary(self) -> u16 {
self.0 >> BOUNDARY_SHIFT
}
pub(crate) fn set_boundary(&mut self, boundary: u16) {
self.0 = (self.0 & RECORD_MASK) | (boundary & 0b11) << BOUNDARY_SHIFT;
}
pub(crate) fn use_class(self) -> (UseClass, bool, bool) {
let r = self.record();
(
r.use_class,
r.flags.needs_decomp(),
r.flags.is_extended_pictographic(),
)
}
pub(crate) fn myanmar_class(self) -> (MyanmarClass, bool) {
let r = self.record();
(r.myanmar_class, r.flags.is_extended_pictographic())
}
pub(crate) fn cluster_class(self) -> (ClusterBreak, bool) {
let r = self.record();
(r.cluster_break, r.flags.is_extended_pictographic())
}
#[inline(always)]
fn record(self) -> &'static Record {
unsafe { RECORDS.get_unchecked((self.0 & RECORD_MASK) as usize) }
}
}
impl From<char> for Properties {
fn from(ch: char) -> Self {
Self::new(ch as u32)
}
}
impl From<&'_ char> for Properties {
fn from(ch: &'_ char) -> Self {
Self::new(*ch as u32)
}
}
impl From<u32> for Properties {
fn from(ch: u32) -> Self {
Self::new(ch)
}
}
impl From<&'_ u32> for Properties {
fn from(ch: &'_ u32) -> Self {
Self::new(*ch)
}
}
pub trait Codepoint: Sized + Copy {
fn properties(self) -> Properties;
fn category(self) -> Category {
self.properties().category()
}
fn block(self) -> Block {
self.properties().block()
}
fn script(self) -> Script {
self.properties().script()
}
fn combining_class(self) -> u8 {
self.properties().combining_class()
}
fn bidi_class(self) -> BidiClass {
self.properties().bidi_class()
}
fn joining_type(self) -> JoiningType {
self.properties().joining_type()
}
fn cluster_break(self) -> ClusterBreak {
self.properties().cluster_break()
}
fn word_break(self) -> WordBreak {
self.properties().word_break()
}
fn line_break(self) -> LineBreak {
self.properties().line_break()
}
fn is_emoji(self) -> bool {
self.properties().is_emoji()
}
fn is_extended_pictographic(self) -> bool {
self.properties().is_extended_pictographic()
}
fn bracket_type(self) -> BracketType;
fn opening_bracket(self) -> Option<char>;
fn closing_bracket(self) -> Option<char>;
fn mirror(self) -> Option<char>;
fn compose(a: char, b: char) -> Option<char>;
fn decompose(self) -> Decompose;
fn decompose_compatible(self) -> Decompose;
}
impl Codepoint for char {
fn properties(self) -> Properties {
Properties::from(self)
}
fn bracket_type(self) -> BracketType {
match self.closing_bracket() {
Some(other) => BracketType::Open(other),
_ => match self.opening_bracket() {
Some(other) => BracketType::Close(other),
_ => BracketType::None,
},
}
}
fn opening_bracket(self) -> Option<char> {
let c = self as u32;
if let Ok(idx) = BRACKETS.binary_search_by(|x| (x.1 as u32).cmp(&c)) {
return Some(unsafe { from_u32_unchecked(BRACKETS[idx].0 as u32) });
}
None
}
fn closing_bracket(self) -> Option<char> {
let c = self as u32;
if let Ok(idx) = BRACKETS.binary_search_by(|x| (x.0 as u32).cmp(&c)) {
return Some(unsafe { from_u32_unchecked(BRACKETS[idx].1 as u32) });
}
None
}
fn mirror(self) -> Option<char> {
let c = self as u32;
if let Ok(idx) = MIRRORS.binary_search_by(|x| (x.0 as u32).cmp(&c)) {
return Some(unsafe { from_u32_unchecked(MIRRORS[idx].1 as u32) });
}
None
}
fn compose(a: char, b: char) -> Option<char> {
compose_pair(a, b)
}
fn decompose(self) -> Decompose {
decompose(self)
}
fn decompose_compatible(self) -> Decompose {
decompose_compat(self)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum BracketType {
None,
Open(char),
Close(char),
}
impl Script {
pub fn from_opentype(tag: Tag) -> Option<Self> {
match SCRIPTS_BY_TAG.binary_search_by(|x| x.0.cmp(&tag)) {
Ok(index) => Some(SCRIPTS_BY_TAG[index].1),
_ => None,
}
}
pub fn name(self) -> &'static str {
SCRIPT_NAMES[self as usize]
}
pub fn is_complex(self) -> bool {
SCRIPT_COMPLEXITY[self as usize]
}
pub fn is_joined(self) -> bool {
matches!(
self,
Script::Arabic
| Script::Mongolian
| Script::Syriac
| Script::Nko
| Script::PhagsPa
| Script::Mandaic
| Script::Manichaean
| Script::PsalterPahlavi
| Script::Adlam
)
}
pub fn to_opentype(self) -> Tag {
SCRIPT_TAGS[self as usize]
}
}
impl WordBreak {
pub(crate) const fn mask(self) -> u32 {
1 << (self as u32)
}
}
impl BidiClass {
pub const fn mask(self) -> u32 {
1 << (self as u32)
}
pub fn needs_resolution(self) -> bool {
use BidiClass::*;
const OVERRIDE_MASK: u32 = RLE.mask() | LRE.mask() | RLO.mask() | LRO.mask();
const ISOLATE_MASK: u32 = RLI.mask() | LRI.mask() | FSI.mask();
const EXPLICIT_MASK: u32 = OVERRIDE_MASK | ISOLATE_MASK;
const BIDI_MASK: u32 = EXPLICIT_MASK | R.mask() | AL.mask() | AN.mask();
self.mask() & BIDI_MASK != 0
}
}