use std::mem::MaybeUninit;
use crate::{
ffi,
translate::{from_glib, IntoGlib, UnsafeFrom},
UnicodeBreakType, UnicodeScript, UnicodeType,
};
mod sealed {
pub trait Sealed {}
impl Sealed for char {}
}
impl UnsafeFrom<u32> for char {
#[inline]
unsafe fn unsafe_from(t: u32) -> Self {
debug_assert!(
char::try_from(t).is_ok(),
"glib returned an invalid Unicode codepoint"
);
unsafe { char::from_u32_unchecked(t) }
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum DecompositionKind {
Compatibility,
Canonical,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum CharacterDecomposition {
NoDecomposition,
Singleton(char),
Pair(char, char),
}
#[doc(alias = "g_unichar")]
pub trait Unichar: sealed::Sealed + Copy + Into<u32> + UnsafeFrom<u32> {
#[doc(alias = "g_unichar_type")]
#[doc(alias = "unichar_type")]
#[inline]
fn unicode_type(self) -> UnicodeType {
unsafe { from_glib(ffi::g_unichar_type(self.into())) }
}
#[doc(alias = "g_unichar_break_type")]
#[doc(alias = "unichar_break_type")]
#[inline]
fn break_type(self) -> UnicodeBreakType {
unsafe { from_glib(ffi::g_unichar_break_type(self.into())) }
}
#[doc(alias = "g_unichar_get_script")]
#[doc(alias = "unichar_get_script")]
#[inline]
fn script(self) -> UnicodeScript {
unsafe { from_glib(ffi::g_unichar_get_script(self.into())) }
}
#[doc(alias = "g_unichar_combining_class")]
#[doc(alias = "unichar_combining_class")]
#[inline]
fn combining_class(self) -> u8 {
unsafe { ffi::g_unichar_combining_class(self.into()) as u8 }
}
#[doc(alias = "g_unichar_ismark")]
#[doc(alias = "unichar_ismark")]
#[inline]
fn is_mark(self) -> bool {
unsafe { from_glib(ffi::g_unichar_ismark(self.into())) }
}
#[doc(alias = "g_unichar_isgraph")]
#[doc(alias = "unichar_isgraph")]
#[inline]
fn is_graphical(self) -> bool {
unsafe { from_glib(ffi::g_unichar_isgraph(self.into())) }
}
#[doc(alias = "g_unichar_ispunct")]
#[doc(alias = "unichar_ispunct")]
#[inline]
fn is_punctuation(self) -> bool {
unsafe { from_glib(ffi::g_unichar_ispunct(self.into())) }
}
#[doc(alias = "g_unichar_istitle")]
#[doc(alias = "unichar_istitle")]
#[inline]
fn is_titlecase(self) -> bool {
unsafe { from_glib(ffi::g_unichar_istitle(self.into())) }
}
#[doc(alias = "g_unichar_isdefined")]
#[doc(alias = "unichar_isdefined")]
#[inline]
fn is_defined(self) -> bool {
unsafe { from_glib(ffi::g_unichar_isdefined(self.into())) }
}
#[doc(alias = "g_unichar_iswide")]
#[doc(alias = "unichar_iswide")]
#[inline]
fn is_wide(self) -> bool {
unsafe { from_glib(ffi::g_unichar_iswide(self.into())) }
}
#[doc(alias = "g_unichar_iswide_cjk")]
#[doc(alias = "unichar_iswide_cjk")]
#[inline]
fn is_wide_cjk(self) -> bool {
unsafe { from_glib(ffi::g_unichar_iswide_cjk(self.into())) }
}
#[doc(alias = "g_unichar_iszerowidth")]
#[doc(alias = "unichar_iszerowidth")]
#[inline]
fn is_zero_width(self) -> bool {
unsafe { from_glib(ffi::g_unichar_iszerowidth(self.into())) }
}
#[doc(alias = "g_unichar_totitle")]
#[doc(alias = "unichar_totitle")]
#[inline]
fn to_titlecase(self) -> Self {
unsafe { Self::unsafe_from(ffi::g_unichar_totitle(self.into())) }
}
#[doc(alias = "g_unichar_get_mirror_char")]
#[doc(alias = "unichar_get_mirror_char")]
#[inline]
fn mirror_char(self) -> Option<Self> {
unsafe {
let mut mirrored = MaybeUninit::uninit();
let res = from_glib(ffi::g_unichar_get_mirror_char(
self.into(),
mirrored.as_mut_ptr(),
));
if res {
Some(Self::unsafe_from(mirrored.assume_init()))
} else {
None
}
}
}
#[doc(alias = "g_unichar_fully_decompose")]
#[doc(alias = "unichar_fully_decompose")]
#[inline]
fn fully_decompose(self, decomposition_kind: DecompositionKind) -> Vec<Self> {
let compat = match decomposition_kind {
DecompositionKind::Compatibility => true,
DecompositionKind::Canonical => false,
};
let buffer_len = ffi::G_UNICHAR_MAX_DECOMPOSITION_LENGTH as usize;
unsafe {
let mut buffer = Vec::<Self>::with_capacity(buffer_len);
let decomposition_length = ffi::g_unichar_fully_decompose(
self.into(),
compat.into_glib(),
buffer.as_mut_ptr().cast(),
buffer_len,
);
debug_assert!(decomposition_length <= buffer_len);
buffer.set_len(decomposition_length);
buffer
}
}
#[doc(alias = "g_unichar_decompose")]
#[doc(alias = "unichar_decompose")]
#[inline]
fn decompose(self) -> CharacterDecomposition {
unsafe {
let mut a = MaybeUninit::uninit();
let mut b = MaybeUninit::uninit();
let res = from_glib(ffi::g_unichar_decompose(
self.into(),
a.as_mut_ptr(),
b.as_mut_ptr(),
));
if res {
let (a, b) = (a.assume_init(), b.assume_init());
if b == 0 {
CharacterDecomposition::Singleton(char::unsafe_from(a))
} else {
CharacterDecomposition::Pair(char::unsafe_from(a), char::unsafe_from(b))
}
} else {
CharacterDecomposition::NoDecomposition
}
}
}
#[doc(alias = "g_unichar_compose")]
#[doc(alias = "unichar_compose")]
#[inline]
fn compose(a: char, b: char) -> Option<Self> {
unsafe {
let mut out = MaybeUninit::uninit();
let res = from_glib(ffi::g_unichar_compose(a.into(), b.into(), out.as_mut_ptr()));
if res {
Some(Self::unsafe_from(out.assume_init()))
} else {
None
}
}
}
}
impl Unichar for char {}