use core::ffi::CStr;
use std::ptr::NonNull;
use rustmex_core::pointers::{MxArray, MatlabPtr, MutMatlabPtr};
use rustmex_core::mxArray;
use rustmex_core::shim::{
rustmex_get_property as mxGetProperty,
rustmex_set_property as mxSetProperty,
rustmex_is_class as mxIsClass,
};
pub use super::index::Index;
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct ObjectArray<P>(P);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ObjectError {
OutOfBounds,
NotAccessible,
}
pub fn is_object(mx: &mxArray) -> bool {
mx.raw_class_id() > if !cfg!(feature = "octave") {
::rustmex_core::raw::mxClassID_mxOBJECT_CLASS
} else {
::rustmex_core::raw::mxClassID_mxFUNCTION_CLASS
}
}
impl<P> std::ops::Deref for ObjectArray<P> where P: MatlabPtr {
type Target = mxArray;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<P> std::ops::DerefMut for ObjectArray<P> where P: MutMatlabPtr {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<'p, P: MatlabPtr + 'p> ObjectArray<P> {
pub fn new(mx: P, classname: &CStr) -> Result<Self, P> {
if !is_object(mx.deref()) || unsafe { mxIsClass(mx.deref(), classname.as_ptr()) } {
return Err(mx)
}
Ok(ObjectArray(mx))
}
pub fn get<I: Index>(&self, idx: I, field: &CStr) -> Result<MxArray, ObjectError> {
let idx = idx.index_into(&self.0).ok_or(ObjectError::OutOfBounds)?;
unsafe { NonNull::new(mxGetProperty(self.0.deref(), idx, field.as_ptr())) }
.map(|mx| unsafe { MxArray::assume_responsibility_ptr(mx.as_ptr()) })
.ok_or(ObjectError::NotAccessible)
}
}
impl<'p, P: MutMatlabPtr + 'p> ObjectArray<P> {
pub fn set<I: Index>(&mut self, idx: I, field: &CStr, val: &mxArray) -> Result<(), ObjectError> {
let idx = idx.index_into(&self.0).ok_or(ObjectError::OutOfBounds)?;
unsafe { mxSetProperty(self.0.deref_mut(), idx, field.as_ptr(), val) };
Ok(())
}
}