use core::ptr::NonNull;
use std::ffi::{CStr, CString, c_void};
use std::os::raw::c_char;
use crate::ffi::{
noesis_base_component_release, noesis_base_component_type_name, noesis_get_xaml_dependencies,
noesis_gui_load_xaml_component,
};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum XamlDependencyKind {
Filename,
Font,
UserControl,
Root,
}
impl XamlDependencyKind {
fn from_raw(kind: i32) -> Option<Self> {
match kind {
0 => Some(Self::Filename),
1 => Some(Self::Font),
2 => Some(Self::UserControl),
3 => Some(Self::Root),
_ => None,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct XamlDependency {
pub uri: String,
pub kind: XamlDependencyKind,
}
unsafe extern "C" fn collect(user: *mut c_void, uri: *const c_char, kind: i32) {
crate::panic_guard::guard(|| {
let out = unsafe { &mut *user.cast::<Vec<XamlDependency>>() };
let Some(kind) = XamlDependencyKind::from_raw(kind) else {
return;
};
let uri = if uri.is_null() {
String::new()
} else {
unsafe { CStr::from_ptr(uri) }
.to_string_lossy()
.into_owned()
};
out.push(XamlDependency { uri, kind });
})
}
#[must_use]
pub fn get_xaml_dependencies(xaml: &[u8], base_uri: &str) -> Vec<XamlDependency> {
let base = CString::new(base_uri).expect("base_uri contained interior NUL");
let mut out: Vec<XamlDependency> = Vec::new();
let out_ptr: *mut Vec<XamlDependency> = &mut out;
unsafe {
noesis_get_xaml_dependencies(
xaml.as_ptr(),
u32::try_from(xaml.len()).expect("XAML > 4 GiB"),
base.as_ptr(),
out_ptr.cast(),
collect,
);
}
out
}
pub struct LoadedComponent {
ptr: NonNull<c_void>,
}
unsafe impl Send for LoadedComponent {}
impl LoadedComponent {
#[must_use]
pub fn raw(&self) -> *mut c_void {
self.ptr.as_ptr()
}
#[must_use]
pub fn type_name(&self) -> String {
let name = unsafe { noesis_base_component_type_name(self.ptr.as_ptr()) };
if name.is_null() {
String::new()
} else {
unsafe { CStr::from_ptr(name) }
.to_string_lossy()
.into_owned()
}
}
}
impl Drop for LoadedComponent {
fn drop(&mut self) {
unsafe { noesis_base_component_release(self.ptr.as_ptr()) };
}
}
#[must_use]
pub fn load_xaml_component(uri: &str) -> Option<LoadedComponent> {
let c = CString::new(uri).expect("uri contained interior NUL");
let ptr = unsafe { noesis_gui_load_xaml_component(c.as_ptr()) };
NonNull::new(ptr).map(|ptr| LoadedComponent { ptr })
}