#![allow(unsafe_op_in_unsafe_fn)]
use core::ffi::CStr;
use core::ptr::{self, NonNull};
use std::ffi::{CString, c_void};
use crate::ffi::{
EnumValue, noesis_base_component_release, noesis_enum_name_from_value,
noesis_enum_value_from_name, noesis_factory_is_registered, noesis_raise_routed_event,
noesis_register_enum, noesis_register_routed_event, noesis_type_add_depends_on,
noesis_type_converter_from_string, noesis_type_get_content_property,
noesis_type_get_depends_on, noesis_type_set_content_property, noesis_unbox_bool,
noesis_unbox_double, noesis_unbox_int32, noesis_unbox_string,
};
use crate::view::FrameworkElement;
pub struct EnumType {
name: CString,
}
#[must_use]
pub fn register_enum(name: &str, variants: &[(&str, i32)]) -> Option<EnumType> {
let cname = CString::new(name).expect("enum name contained NUL");
let owned: Vec<CString> = variants
.iter()
.map(|(n, _)| CString::new(*n))
.collect::<Result<_, _>>()
.ok()?;
let ffi_values: Vec<EnumValue> = owned
.iter()
.zip(variants.iter())
.map(|(c, (_, v))| EnumValue {
name: c.as_ptr(),
value: *v,
})
.collect();
let ty = unsafe {
noesis_register_enum(cname.as_ptr(), ffi_values.as_ptr(), ffi_values.len() as u32)
};
if ty.is_null() {
return None;
}
Some(EnumType { name: cname })
}
impl EnumType {
#[must_use]
pub fn name(&self) -> &str {
self.name.to_str().unwrap_or_default()
}
#[must_use]
pub fn value_from_name(&self, variant_name: &str) -> Option<i32> {
let cn = CString::new(variant_name).ok()?;
let mut out = 0i32;
let ok = unsafe { noesis_enum_value_from_name(self.name.as_ptr(), cn.as_ptr(), &mut out) };
ok.then_some(out)
}
#[must_use]
pub fn name_from_value(&self, value: i32) -> Option<String> {
let mut out: *const core::ffi::c_char = ptr::null();
let ok = unsafe { noesis_enum_name_from_value(self.name.as_ptr(), value, &mut out) };
if !ok || out.is_null() {
return None;
}
Some(
unsafe { CStr::from_ptr(out) }
.to_string_lossy()
.into_owned(),
)
}
}
#[repr(i32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum RoutingStrategy {
Tunnel = 0,
Bubble = 1,
Direct = 2,
}
#[must_use]
pub fn register_routed_event(type_name: &str, event_name: &str, strategy: RoutingStrategy) -> bool {
let ct = CString::new(type_name).expect("type name contained NUL");
let ce = CString::new(event_name).expect("event name contained NUL");
unsafe { noesis_register_routed_event(ct.as_ptr(), ce.as_ptr(), strategy as i32) }
}
#[must_use]
pub fn raise_event(element: &FrameworkElement, event_name: &str) -> bool {
let ce = CString::new(event_name).expect("event name contained NUL");
unsafe { noesis_raise_routed_event(element.raw(), ce.as_ptr()) }
}
#[must_use]
pub fn is_component_registered(name: &str) -> bool {
let Ok(c) = CString::new(name) else {
return false;
};
unsafe { noesis_factory_is_registered(c.as_ptr()) }
}
#[must_use]
pub fn set_content_property(type_name: &str, prop_name: &str) -> bool {
let ct = CString::new(type_name).expect("type name contained NUL");
let cp = CString::new(prop_name).expect("prop name contained NUL");
unsafe { noesis_type_set_content_property(ct.as_ptr(), cp.as_ptr()) }
}
#[must_use]
pub fn get_content_property(type_name: &str) -> Option<String> {
let ct = CString::new(type_name).expect("type name contained NUL");
let mut out: *const core::ffi::c_char = ptr::null();
let ok = unsafe { noesis_type_get_content_property(ct.as_ptr(), &mut out) };
if !ok || out.is_null() {
return None;
}
Some(
unsafe { CStr::from_ptr(out) }
.to_string_lossy()
.into_owned(),
)
}
#[must_use]
pub fn add_depends_on(type_name: &str, prop_name: &str) -> bool {
let ct = CString::new(type_name).expect("type name contained NUL");
let cp = CString::new(prop_name).expect("prop name contained NUL");
unsafe { noesis_type_add_depends_on(ct.as_ptr(), cp.as_ptr()) }
}
#[must_use]
pub fn get_depends_on(type_name: &str) -> Option<String> {
let ct = CString::new(type_name).expect("type name contained NUL");
let mut out: *const core::ffi::c_char = ptr::null();
let ok = unsafe { noesis_type_get_depends_on(ct.as_ptr(), &mut out) };
if !ok || out.is_null() {
return None;
}
Some(
unsafe { CStr::from_ptr(out) }
.to_string_lossy()
.into_owned(),
)
}
pub struct BoxedValue {
ptr: NonNull<c_void>,
}
unsafe impl Send for BoxedValue {}
impl BoxedValue {
#[must_use]
pub fn raw(&self) -> *mut c_void {
self.ptr.as_ptr()
}
#[must_use]
pub fn as_i32(&self) -> Option<i32> {
let mut out = 0i32;
let ok = unsafe { noesis_unbox_int32(self.ptr.as_ptr(), &mut out) };
ok.then_some(out)
}
#[must_use]
pub fn as_bool(&self) -> Option<bool> {
let mut out = false;
let ok = unsafe { noesis_unbox_bool(self.ptr.as_ptr(), &mut out) };
ok.then_some(out)
}
#[must_use]
pub fn as_f64(&self) -> Option<f64> {
let mut out = 0.0f64;
let ok = unsafe { noesis_unbox_double(self.ptr.as_ptr(), &mut out) };
ok.then_some(out)
}
#[must_use]
pub fn as_str(&self) -> Option<&str> {
let s = unsafe { noesis_unbox_string(self.ptr.as_ptr()) };
if s.is_null() {
return None;
}
unsafe { CStr::from_ptr(s) }.to_str().ok()
}
}
impl Drop for BoxedValue {
fn drop(&mut self) {
unsafe { noesis_base_component_release(self.ptr.as_ptr()) }
}
}
#[must_use]
pub fn convert_from_string(type_name: &str, s: &str) -> Option<BoxedValue> {
let ct = CString::new(type_name).expect("type name contained NUL");
let cs = CString::new(s).expect("string contained NUL");
let mut out: *mut c_void = ptr::null_mut();
let ok = unsafe { noesis_type_converter_from_string(ct.as_ptr(), cs.as_ptr(), &mut out) };
if !ok {
return None;
}
NonNull::new(out).map(|ptr| BoxedValue { ptr })
}