use std::ptr::NonNull;
use std::ops::DerefMut;
use crate::convert::FromMatlab;
use rustmex_core::shim::{
rustmex_get_cell as mxGetCell,
rustmex_set_cell as mxSetCell,
rustmex_create_cell_array as mxCreateCellArray,
};
use rustmex_core::classid::ClassID;
use rustmex_core::raw::{
mwIndex,
};
use rustmex_core::convert::{FromMatlabError};
use rustmex_core::{mxArray, pointers::{MxArray, MatlabPtr, MutMatlabPtr},};
use rustmex_core::{
MatlabClass,
MutMatlabClass,
OwnedMatlabClass,
NewEmpty,
};
use super::index::Index;
#[derive(Debug, Clone, Copy)]
#[repr(transparent)]
pub struct CellArray<P: MatlabPtr>(P);
impl<'a> FromMatlab<'a> for CellArray<&'a mxArray> {
fn from_matlab(mx: &'a mxArray) -> Result<Self, FromMatlabError<&'a mxArray>> {
Self::from_mx_array(mx)
}
}
impl<P> std::ops::Deref for CellArray<P> where P: MatlabPtr {
type Target = mxArray;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<P> std::ops::DerefMut for CellArray<P> where P: MutMatlabPtr {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<P> MatlabClass<P> for CellArray<P> where P: MatlabPtr {
fn from_mx_array(mx: P) -> Result<Self, FromMatlabError<P>> {
if mx.class_id() == Ok(ClassID::Cell) {
Ok(Self(mx))
} else {
Err(FromMatlabError::new_badclass(mx))
}
}
fn into_inner(self) -> P {
self.0
}
fn inner(&self) -> &P {
&self.0
}
type Owned = CellArray<MxArray>;
fn duplicate(&self) -> Self::Owned {
CellArray(self.0.duplicate())
}
}
impl<P> MutMatlabClass<P> for CellArray<P> where P: MutMatlabPtr {
type AsBorrowed<'a> = CellArray<&'a mxArray> where Self: 'a;
fn as_borrowed<'a>(&'a self) -> Self::AsBorrowed<'a> {
CellArray(self.0.deref())
}
fn inner_mut(&mut self) -> &mut P {
&mut self.0
}
}
impl OwnedMatlabClass for CellArray<MxArray> {
type AsMutable<'a> = CellArray<&'a mut mxArray> where Self: 'a;
fn as_mutable<'a>(&'a mut self) -> Self::AsMutable<'a> {
CellArray(self.0.deref_mut())
}
}
impl NewEmpty for CellArray<MxArray> {
fn new_empty() -> Self {
const EMPTY: [usize; 2] = [0, 0];
Self::new(&EMPTY)
}
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
#[non_exhaustive]
pub enum CellError {
OutOfBounds,
}
impl<'a, P: MatlabPtr + 'a> CellArray<P> {
pub fn get<I: Index>(&self, idx: I) -> Result<Option<&'a mxArray>, CellError> {
Ok(self.get_core(idx)?.map(|ptr| unsafe { ptr.as_ref() }))
}
fn get_core<I: Index>(&self, idx: I) -> Result<Option<NonNull<mxArray>>, CellError> {
let idx = idx.index_into(self).ok_or(CellError::OutOfBounds)?;
Ok(NonNull::new(unsafe { mxGetCell(self.0.as_ref(), idx as mwIndex) }))
}
pub fn comma_separated_list(&self) -> impl Iterator<Item = Option<&'a mxArray>> + '_ {
(0..self.numel()).map(|idx| self.get(idx).unwrap())
}
}
impl<'a, P: MutMatlabPtr + 'a> CellArray<P> {
pub fn get_mut<I: Index>(&mut self, idx: I) -> Result<Option<&'a mut mxArray>, CellError> {
Ok(self.get_core(idx)?.map(|mut ptr| unsafe { ptr.as_mut() }))
}
pub fn replace<I: Index>(&mut self, idx: I, mx: Option<MxArray>) -> Result<Option<MxArray>, CellError> {
let idx = idx.index_into(self).ok_or(CellError::OutOfBounds)?;
let old = unsafe { NonNull::new(mxGetCell(self.0.deref_mut(), idx as _)) }
.map(|mut ptr| unsafe { MxArray::assume_responsibility(ptr.as_mut()) } );
unsafe { mxSetCell(
self.0.deref_mut(),
idx as _,
if let Some(val) = mx {
MxArray::transfer_responsibility_ptr(val)
} else {
std::ptr::null_mut()
}
)
}
Ok(old)
}
#[inline]
pub fn set<I: Index>(&mut self, idx: I, mx: MxArray) -> Result<Option<MxArray>, CellError> {
self.replace(idx, Some(mx))
}
#[inline]
pub fn unset<I: Index>(&mut self, idx: I) -> Result<Option<MxArray>, CellError> {
self.replace(idx, None)
}
}
impl CellArray<MxArray> {
pub fn new(shape: &[usize]) -> Self {
rustmex_core::shape_ok!(shape);
let ptr = unsafe { mxCreateCellArray(
shape.len(),
shape.as_ptr(),
)};
if ptr.is_null() {
panic!("OOM")
}
Self(unsafe { MxArray::assume_responsibility(&mut *ptr) } )
}
}