1use std::ptr::NonNull;
7use std::ops::DerefMut;
8use crate::convert::FromMatlab;
9
10use rustmex_core::shim::{
11 rustmex_get_cell as mxGetCell,
12 rustmex_set_cell as mxSetCell,
13 rustmex_create_cell_array as mxCreateCellArray,
14};
15
16use rustmex_core::classid::ClassID;
17
18use rustmex_core::raw::{
19 mwIndex,
20};
21use rustmex_core::convert::{FromMatlabError};
22use rustmex_core::{mxArray, pointers::{MxArray, MatlabPtr, MutMatlabPtr},};
23use rustmex_core::{
24 MatlabClass,
25 MutMatlabClass,
26 OwnedMatlabClass,
27 NewEmpty,
28};
29
30use super::index::Index;
31
32#[derive(Debug, Clone, Copy)]
36#[repr(transparent)]
37pub struct CellArray<P: MatlabPtr>(P);
38
39impl<'a> FromMatlab<'a> for CellArray<&'a mxArray> {
40 fn from_matlab(mx: &'a mxArray) -> Result<Self, FromMatlabError<&'a mxArray>> {
41 Self::from_mx_array(mx)
42 }
43}
44
45impl<P> std::ops::Deref for CellArray<P> where P: MatlabPtr {
46 type Target = mxArray;
47
48 fn deref(&self) -> &Self::Target {
49 &self.0
50 }
51}
52
53impl<P> std::ops::DerefMut for CellArray<P> where P: MutMatlabPtr {
54 fn deref_mut(&mut self) -> &mut Self::Target {
55 &mut self.0
56 }
57}
58
59impl<P> MatlabClass<P> for CellArray<P> where P: MatlabPtr {
60 fn from_mx_array(mx: P) -> Result<Self, FromMatlabError<P>> {
61 if mx.class_id() == Ok(ClassID::Cell) {
62 Ok(Self(mx))
63 } else {
64 Err(FromMatlabError::new_badclass(mx))
65 }
66 }
67
68 fn into_inner(self) -> P {
69 self.0
70 }
71
72 fn inner(&self) -> &P {
73 &self.0
74 }
75
76 type Owned = CellArray<MxArray>;
77 fn duplicate(&self) -> Self::Owned {
78 CellArray(self.0.duplicate())
79 }
80}
81
82impl<P> MutMatlabClass<P> for CellArray<P> where P: MutMatlabPtr {
83 type AsBorrowed<'a> = CellArray<&'a mxArray> where Self: 'a;
84 fn as_borrowed<'a>(&'a self) -> Self::AsBorrowed<'a> {
85 CellArray(self.0.deref())
86 }
87
88 fn inner_mut(&mut self) -> &mut P {
89 &mut self.0
90 }
91}
92
93impl OwnedMatlabClass for CellArray<MxArray> {
94 type AsMutable<'a> = CellArray<&'a mut mxArray> where Self: 'a;
95 fn as_mutable<'a>(&'a mut self) -> Self::AsMutable<'a> {
96 CellArray(self.0.deref_mut())
97 }
98}
99
100impl NewEmpty for CellArray<MxArray> {
101 fn new_empty() -> Self {
102 const EMPTY: [usize; 2] = [0, 0];
103 Self::new(&EMPTY)
104 }
105}
106
107#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
108#[non_exhaustive]
109pub enum CellError {
110 OutOfBounds,
112}
113
114impl<'a, P: MatlabPtr + 'a> CellArray<P> {
115 pub fn get<I: Index>(&self, idx: I) -> Result<Option<&'a mxArray>, CellError> {
123 Ok(self.get_core(idx)?.map(|ptr| unsafe { ptr.as_ref() }))
124 }
125
126 fn get_core<I: Index>(&self, idx: I) -> Result<Option<NonNull<mxArray>>, CellError> {
130 let idx = idx.index_into(self).ok_or(CellError::OutOfBounds)?;
131
132 Ok(NonNull::new(unsafe { mxGetCell(self.0.as_ref(), idx as mwIndex) }))
133 }
134
135 pub fn comma_separated_list(&self) -> impl Iterator<Item = Option<&'a mxArray>> + '_ {
141 (0..self.numel()).map(|idx| self.get(idx).unwrap())
142 }
143}
144
145impl<'a, P: MutMatlabPtr + 'a> CellArray<P> {
146 pub fn get_mut<I: Index>(&mut self, idx: I) -> Result<Option<&'a mut mxArray>, CellError> {
148 Ok(self.get_core(idx)?.map(|mut ptr| unsafe { ptr.as_mut() }))
149 }
150
151 pub fn replace<I: Index>(&mut self, idx: I, mx: Option<MxArray>) -> Result<Option<MxArray>, CellError> {
156 let idx = idx.index_into(self).ok_or(CellError::OutOfBounds)?;
157
158 let old = unsafe { NonNull::new(mxGetCell(self.0.deref_mut(), idx as _)) }
159 .map(|mut ptr| unsafe { MxArray::assume_responsibility(ptr.as_mut()) } );
160
161 unsafe { mxSetCell(
162 self.0.deref_mut(),
163 idx as _,
164 if let Some(val) = mx {
165 MxArray::transfer_responsibility_ptr(val)
166 } else {
167 std::ptr::null_mut()
168 }
169 )
170 }
171
172 Ok(old)
173 }
174
175 #[inline]
177 pub fn set<I: Index>(&mut self, idx: I, mx: MxArray) -> Result<Option<MxArray>, CellError> {
178 self.replace(idx, Some(mx))
179 }
180
181 #[inline]
183 pub fn unset<I: Index>(&mut self, idx: I) -> Result<Option<MxArray>, CellError> {
184 self.replace(idx, None)
185 }
186}
187
188impl CellArray<MxArray> {
189 pub fn new(shape: &[usize]) -> Self {
193 rustmex_core::shape_ok!(shape);
194
195 let ptr = unsafe { mxCreateCellArray(
196 shape.len(),
197 shape.as_ptr(),
198 )};
199
200 if ptr.is_null() {
201 panic!("OOM")
202 }
203
204 Self(unsafe { MxArray::assume_responsibility(&mut *ptr) } )
205 }
206}