rustmex 0.6.4

Rustmex: providing convenient Rust bindings to Matlab MEX API's
Documentation
/*!
 * Matlab object arrays/custom classes
 */
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;

/**
 * Wrapper type for Matlab objects.
 *
 * As exposed in the C-API, Matlab Objects are far from useful. A get makes a full copy
 * of the property you're trying to access; there is no documented method of getting a
 * reference. Hence the sparse implementation
 */
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct ObjectArray<P>(P);

/**
 * List of possible errors that can be encountered working with [`ObjectArray`]s.
 */
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ObjectError {
	/// The object element in the object array you're trying to access is out of
	/// bounds of the array.
	OutOfBounds,
	/// The property is not accessible, either due to not existing or being private
	/// or protected.
	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))
	}

	/**
	 * Get a copy of the value of a property at the specified index in the array.
	 */
	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> {
	/**
	 * Assign a value to a property of the element at the specified index. The value
	 * does not have to be owned; Matlab will make a copy.
	 *
	 * Besides out-of-bound errors, it is impossible to determine whether the
	 * assignment succeeded.
	 */
	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(())
	}
}