use alloc::ffi::CString;
use alloc::string::String;
use crate::error::{Error, Result};
use crate::paint::{BorrowedPaint, Paint};
use thorvg_sys as sys;
pub struct Accessor<'eng> {
raw: sys::Tvg_Accessor,
_engine: core::marker::PhantomData<&'eng ()>,
}
impl Accessor<'_> {
pub(crate) fn new() -> Result<Self> {
let raw = unsafe { sys::tvg_accessor_new() };
if raw.is_null() {
return Err(Error::FailedAllocation);
}
Ok(Self {
raw,
_engine: core::marker::PhantomData,
})
}
pub fn for_each<P, F>(&mut self, paint: &P, func: F) -> Result<()>
where
P: Paint,
F: FnMut(BorrowedAccessor<'_>, BorrowedPaint<'_>) -> bool,
{
struct Ctx<F> {
func: F,
acc_raw: sys::Tvg_Accessor,
}
unsafe extern "C" fn trampoline<F>(
paint_raw: sys::Tvg_Paint,
data: *mut core::ffi::c_void,
) -> bool
where
F: FnMut(BorrowedAccessor<'_>, BorrowedPaint<'_>) -> bool,
{
let ctx = unsafe { &mut *data.cast::<Ctx<F>>() };
let acc_view = unsafe { BorrowedAccessor::from_raw(ctx.acc_raw) };
let paint_view = unsafe { BorrowedPaint::from_raw(paint_raw) };
invoke_user(&mut ctx.func, acc_view, paint_view)
}
let mut ctx = Ctx {
func,
acc_raw: self.raw,
};
let data = core::ptr::from_mut(&mut ctx).cast::<core::ffi::c_void>();
Error::from_raw(unsafe {
sys::tvg_accessor_set(self.raw, paint.raw(), Some(trampoline::<F>), data)
})
}
pub fn generate_id(name: &str) -> Option<u32> {
let c_name = CString::new(name).ok()?;
Some(unsafe { sys::tvg_accessor_generate_id(c_name.as_ptr()) })
}
}
#[cfg(feature = "std")]
fn invoke_user<F>(f: &mut F, acc: BorrowedAccessor<'_>, paint: BorrowedPaint<'_>) -> bool
where
F: FnMut(BorrowedAccessor<'_>, BorrowedPaint<'_>) -> bool,
{
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| f(acc, paint))).unwrap_or(false)
}
#[cfg(not(feature = "std"))]
fn invoke_user<F>(f: &mut F, acc: BorrowedAccessor<'_>, paint: BorrowedPaint<'_>) -> bool
where
F: FnMut(BorrowedAccessor<'_>, BorrowedPaint<'_>) -> bool,
{
f(acc, paint)
}
impl Drop for Accessor<'_> {
fn drop(&mut self) {
unsafe {
sys::tvg_accessor_del(self.raw);
}
}
}
impl core::fmt::Debug for Accessor<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Accessor").finish_non_exhaustive()
}
}
pub struct BorrowedAccessor<'a> {
raw: sys::Tvg_Accessor,
_life: core::marker::PhantomData<&'a ()>,
}
impl BorrowedAccessor<'_> {
unsafe fn from_raw(raw: sys::Tvg_Accessor) -> Self {
Self {
raw,
_life: core::marker::PhantomData,
}
}
pub fn get_name(&self, id: u32) -> Option<String> {
let ptr = unsafe { sys::tvg_accessor_get_name(self.raw, id) };
if ptr.is_null() {
None
} else {
Some(
unsafe { core::ffi::CStr::from_ptr(ptr) }
.to_string_lossy()
.into_owned(),
)
}
}
}
impl core::fmt::Debug for BorrowedAccessor<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("BorrowedAccessor").finish_non_exhaustive()
}
}