rustmex/
cell.rs

1/*!
2 * Cell arrays
3 *
4 */
5
6use 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/**
33 * Wrapper type for a Cell Array.
34 */
35#[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	/// The cell which you're trying to access is out of bounds of the array.
111	OutOfBounds,
112}
113
114impl<'a, P: MatlabPtr + 'a> CellArray<P> {
115	/**
116	 * Get the inner value of some cell in this array at a given index.
117	 *
118	 * This value may not yet be initialised, in which case this method returns
119	 * `Ok(None)`. If the index is out of bounds, it returns
120	 * `Err(CellError::OutOfBounds)`.
121	 */
122	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	/// Shared behaviour for the get(_mut)? methods.
127	///
128	/// If this returns a None, it means that the particular cell was not initialised
129	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	/**
136	 * When one uses the "spread operator" in matlab on a cell array (i.e. `x{:}`) it
137	 * produces a "comma separated list". This method is the Rustmex equivalent;
138	 * iterating over all the cells in the cell array.
139	 */
140	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	/// Get a mutable reference to a child mxArray
147	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	/**
152	 * Replace the current value of the cell at the given index, by optionally a new
153	 * value. Returns the value that was there previously, if there was any.
154	 */
155	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	/// Set the value at the given index. Returns the previous, if there was any.
176	#[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	/// Remove the value (if there is any) at the given index and return it, leaving the cell empty.
182	#[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	/**
190	 * Create a new cell array with the given shape.
191	 */
192	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}