use std::sync::LazyLock;
use crate::error::{self, Error};
use crate::sink;
use crate::sys;
const PLANE: &str = "unicode";
const ORBIT_GUESS: usize = 8;
const PROPERTY_GUESS: usize = 64;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct Codepoints {
lo: u32,
hi: u32,
}
impl Codepoints {
#[must_use]
pub fn low(&self) -> u32 {
self.lo
}
#[must_use]
pub fn high(&self) -> u32 {
self.hi
}
#[must_use]
pub fn count(&self) -> u32 {
self.hi.saturating_sub(self.lo) + 1
}
#[must_use]
pub fn holds(&self, c: char) -> bool {
(self.lo..=self.hi).contains(&u32::from(c))
}
pub fn chars(&self) -> impl Iterator<Item = char> {
(self.lo..=self.hi).filter_map(char::from_u32)
}
}
pub fn orbit(c: char) -> Result<Vec<char>, Error> {
let raw = sink::reap_all(PLANE, ORBIT_GUESS, |out, cap, written| {
unsafe { ffi::irgx_fold_orbit(u32::from(c), out, cap, written) }
})?;
raw.iter()
.map(|cp| {
char::from_u32(*cp).ok_or_else(|| Error::Inconsistent {
message: format!("the fold table names U+{cp:04X}, which is not a scalar value"),
})
})
.collect()
}
pub fn property(name: &str) -> Result<Vec<Codepoints>, Error> {
let name = name.as_bytes();
sink::reap_all(PLANE, PROPERTY_GUESS, |out, cap, written| {
unsafe { ffi::irgx_property_ranges(name.as_ptr(), name.len(), out, cap, written) }
})
}
pub fn holds(name: &str, c: char) -> Result<bool, Error> {
let name = name.as_bytes();
let status = unsafe { ffi::irgx_property_has(name.as_ptr(), name.len(), u32::from(c)) };
if status < 0 {
return Err(error::plane_fault(status, PLANE));
}
Ok(status == sys::MATCH)
}
#[must_use]
pub fn version() -> &'static str {
static VERSION: LazyLock<String> = LazyLock::new(|| {
let mut out = sys::Text::default();
if unsafe { ffi::irgx_unicode_version(&raw mut out) } < 0 {
return String::new();
}
let bytes = unsafe { sys::borrowed(&out) };
String::from_utf8_lossy(bytes).into_owned()
});
&VERSION
}
mod ffi {
use super::{Codepoints, sys};
unsafe extern "C" {
pub fn irgx_fold_orbit(cp: u32, out: *mut u32, cap: usize, written: *mut usize) -> i32;
pub fn irgx_property_ranges(
name: *const u8,
len: usize,
out: *mut Codepoints,
cap: usize,
written: *mut usize,
) -> i32;
pub fn irgx_property_has(name: *const u8, len: usize, cp: u32) -> i32;
pub fn irgx_unicode_version(out: *mut sys::Text) -> i32;
}
}