use std::{
ffi::{CStr, CString},
fmt,
marker::PhantomData,
os::raw::c_char,
};
use once_cell::sync::OnceCell;
use static_assertions::assert_not_impl_any;
use crate::{
rtl,
sys::{self, mcomplex, mint, mreal},
FromArg, Image, NumericArray,
};
#[derive(Debug)]
#[derive(ref_cast::RefCast)]
#[repr(transparent)]
pub struct DataStore(sys::DataStore);
assert_not_impl_any!(DataStore: Copy);
pub struct DataStoreNode<'store> {
raw: sys::DataStoreNode,
marker: PhantomData<&'store DataStore>,
data: OnceCell<sys::MArgument>,
}
#[allow(missing_docs)]
pub enum DataStoreNodeValue<'node> {
Boolean(bool),
Integer(mint),
Real(mreal),
Complex(mcomplex),
Str(&'node str),
NumericArray(&'node NumericArray),
Image(&'node Image),
DataStore(&'node DataStore),
}
pub struct Nodes<'s> {
node: Option<DataStoreNode<'s>>,
}
impl DataStore {
pub fn new() -> Self {
let ds: sys::DataStore = unsafe { rtl::createDataStore() };
if ds.is_null() {
panic!("sys::DataStore is NULL");
}
DataStore(ds)
}
pub fn len(&self) -> usize {
let DataStore(ds) = *self;
let len: i64 = unsafe { rtl::DataStore_getLength(ds) };
usize::try_from(len).expect("DataStore i64 length overflows usize")
}
pub unsafe fn from_raw(raw: sys::DataStore) -> Self {
DataStore(raw)
}
pub fn into_raw(self) -> sys::DataStore {
let DataStore(ds) = self;
std::mem::forget(self);
ds
}
pub fn add_bool(&mut self, value: bool) {
let DataStore(ds) = *self;
unsafe { rtl::DataStore_addBoolean(ds, sys::mbool::from(value)) }
}
pub fn add_i64(&mut self, value: i64) {
let DataStore(ds) = *self;
unsafe { rtl::DataStore_addInteger(ds, value) }
}
pub fn add_f64(&mut self, value: f64) {
let DataStore(ds) = *self;
unsafe { rtl::DataStore_addReal(ds, value) }
}
pub fn add_complex_f64(&mut self, value: sys::mcomplex) {
let DataStore(ds) = *self;
unsafe { rtl::DataStore_addComplex(ds, value) }
}
pub fn add_str(&mut self, value: &str) {
let DataStore(ds) = *self;
let value = CString::new(value).expect("could not convert &str to CString");
unsafe { rtl::DataStore_addString(ds, value.as_ptr() as *mut c_char) }
}
pub fn add_c_str(&mut self, value: &CStr) {
let DataStore(ds) = *self;
unsafe { rtl::DataStore_addString(ds, value.as_ptr() as *mut c_char) }
}
pub fn add_data_store(&mut self, ds: DataStore) {
let DataStore(this_ds) = *self;
let other_ds = ds.into_raw();
unsafe { rtl::DataStore_addDataStore(this_ds, other_ds) }
}
pub fn add_numeric_array(&mut self, array: NumericArray) {
let DataStore(ds) = *self;
let array = unsafe { array.into_raw() };
unsafe { rtl::DataStore_addMNumericArray(ds, array) }
}
pub fn add_named_bool(&mut self, name: &str, value: bool) {
let DataStore(ds) = *self;
let name = CString::new(name).expect("could not convert &str to CString");
unsafe {
rtl::DataStore_addNamedBoolean(
ds,
name.as_ptr() as *mut c_char,
sys::mbool::from(value),
)
}
}
pub fn add_named_i64(&mut self, name: &str, value: i64) {
let DataStore(ds) = *self;
let name = CString::new(name).expect("could not convert &str to CString");
unsafe { rtl::DataStore_addNamedInteger(ds, name.as_ptr() as *mut c_char, value) }
}
pub fn add_named_f64(&mut self, name: &str, value: f64) {
let DataStore(ds) = *self;
let name = CString::new(name).expect("could not convert &str to CString");
unsafe { rtl::DataStore_addNamedReal(ds, name.as_ptr() as *mut c_char, value) }
}
pub fn add_named_complex_f64(&mut self, name: &str, value: sys::mcomplex) {
let DataStore(ds) = *self;
let name = CString::new(name).expect("could not convert &str to CString");
unsafe { rtl::DataStore_addNamedComplex(ds, name.as_ptr() as *mut c_char, value) }
}
pub fn add_named_str(&mut self, name: &str, value: &str) {
let DataStore(ds) = *self;
let name = CString::new(name).expect("could not convert &str to CString");
let value = CString::new(value).expect("could not convert &str to CString");
unsafe {
rtl::DataStore_addNamedString(
ds,
name.as_ptr() as *mut c_char,
value.as_ptr() as *mut c_char,
)
}
}
pub fn add_named_c_str(&mut self, name: &str, value: &CStr) {
let DataStore(ds) = *self;
let name = CString::new(name).expect("could not convert &str to CString");
unsafe {
rtl::DataStore_addNamedString(
ds,
name.as_ptr() as *mut c_char,
value.as_ptr() as *mut c_char,
)
}
}
pub fn add_named_data_store(&mut self, name: &str, ds: DataStore) {
let DataStore(this_ds) = *self;
let other_ds = ds.into_raw();
let name = CString::new(name).expect("could not convert &str to CString");
unsafe {
rtl::DataStore_addNamedDataStore(
this_ds,
name.as_ptr() as *mut c_char,
other_ds,
)
}
}
pub fn add_named_numeric_array(&mut self, name: &str, array: NumericArray) {
let DataStore(ds) = *self;
let array = unsafe { array.into_raw() };
let name = CString::new(name).expect("could not convert &str to CString");
unsafe {
rtl::DataStore_addNamedMNumericArray(ds, name.as_ptr() as *mut c_char, array)
}
}
pub fn nodes<'s>(&'s self) -> Nodes<'s> {
Nodes {
node: self.first_node(),
}
}
pub fn first_node<'s>(&'s self) -> Option<DataStoreNode<'s>> {
let DataStore(raw) = *self;
let node = unsafe { rtl::DataStore_getFirstNode(raw) };
if node.is_null() {
return None;
}
Some(DataStoreNode {
raw: node,
marker: PhantomData,
data: OnceCell::new(),
})
}
}
impl<'store> DataStoreNode<'store> {
pub fn name(&self) -> Option<String> {
let mut raw_c_str: *mut c_char = std::ptr::null_mut();
let err_code: sys::errcode_t =
unsafe { rtl::DataStoreNode_getName(self.raw, &mut raw_c_str) };
if err_code != 0 || raw_c_str.is_null() {
return None;
}
let c_str = unsafe { CStr::from_ptr(raw_c_str) };
let str: &str = c_str.to_str().ok()?;
Some(str.to_owned())
}
pub fn value<'node>(&'node self) -> DataStoreNodeValue<'node> {
use DataStoreNodeValue as V;
let data_raw: &'node sys::MArgument = unsafe { self.data_raw() };
unsafe {
match self.data_type_raw() as u32 {
sys::MType_Undef => panic!("unexpected DataStoreNode Undef data type"),
sys::MType_Boolean => V::Boolean(bool::from_arg(data_raw)),
sys::MType_Integer => V::Integer(mint::from_arg(data_raw)),
sys::MType_Real => V::Real(mreal::from_arg(data_raw)),
sys::MType_Complex => V::Complex(mcomplex::from_arg(data_raw)),
sys::MType_UTF8String => V::Str(<&str>::from_arg(data_raw)),
sys::MType_Tensor => {
unimplemented!("unhandled DataStoreNode Tensor data type")
},
sys::MType_SparseArray => {
unimplemented!("unhandled DataStoreNode SparseArray data type")
},
sys::MType_NumericArray => {
V::NumericArray(<&NumericArray>::from_arg(data_raw))
},
sys::MType_Image => V::Image(<&Image>::from_arg(data_raw)),
sys::MType_DataStore => V::DataStore(<&DataStore>::from_arg(data_raw)),
type_ => {
panic!("unexpected DataStoreNode::data_type_raw() value: {}", type_)
},
}
}
}
pub fn next_node(&self) -> Option<DataStoreNode<'store>> {
let raw_next: sys::DataStoreNode =
unsafe { rtl::DataStoreNode_getNextNode(self.raw) };
if raw_next.is_null() {
return None;
}
Some(DataStoreNode {
raw: raw_next,
marker: PhantomData,
data: OnceCell::new(),
})
}
pub fn data_type_raw(&self) -> sys::type_t {
unsafe { rtl::DataStoreNode_getDataType(self.raw) }
}
pub unsafe fn data_raw(&self) -> &sys::MArgument {
match self.try_data_raw() {
Ok(value) => value,
Err(code) => panic!(
"DataStoreNode::data_raw: failed to get data (error code: {})",
code
),
}
}
pub unsafe fn try_data_raw<'node>(
&'node self,
) -> Result<&'node sys::MArgument, sys::errcode_t> {
self.data
.get_or_try_init(|| -> Result<sys::MArgument, sys::errcode_t> {
let mut arg: sys::MArgument = sys::MArgument {
integer: std::ptr::null_mut(),
};
let err_code: sys::errcode_t =
rtl::DataStoreNode_getData(self.raw, &mut arg);
if err_code != 0 {
return Err(err_code);
}
Ok(arg)
})
}
}
impl<'store> Iterator for Nodes<'store> {
type Item = DataStoreNode<'store>;
fn next(&mut self) -> Option<Self::Item> {
let Nodes { node } = self;
let curr = node.take()?;
*node = curr.next_node();
Some(curr)
}
}
impl Clone for DataStore {
fn clone(&self) -> DataStore {
let DataStore(ds) = *self;
let duplicate = unsafe { rtl::copyDataStore(ds) };
DataStore(duplicate)
}
}
impl Drop for DataStore {
fn drop(&mut self) {
let DataStore(ds) = *self;
let ds: sys::DataStore = ds;
unsafe { rtl::deleteDataStore(ds) }
}
}
impl<'store> fmt::Debug for DataStoreNode<'store> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("DataStoreNode")
.field("name", &self.name())
.field("value", &self.value())
.finish()
}
}
impl<'node> fmt::Debug for DataStoreNodeValue<'node> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use DataStoreNodeValue as V;
match self {
V::Boolean(val) => val.fmt(f),
V::Integer(val) => val.fmt(f),
V::Real(val) => val.fmt(f),
V::Complex(val) => val.fmt(f),
V::Str(val) => val.fmt(f),
V::NumericArray(val) => val.fmt(f),
V::Image(val) => val.fmt(f),
V::DataStore(val) => val.fmt(f),
}
}
}