rustmex 0.6.4

Rustmex: providing convenient Rust bindings to Matlab MEX API's
Documentation
/*!
 * Convert directly to and from matlab pointers
 *
 * This module is basically one big shortcut. Instead of going via the conversion traits
 * defined on all the numeric types, the implementations in this module allow you to go
 * directly
 */
use ndarray::{ArrayViewD, Array, Dimension};

#[allow(unused)]
use ndarray::{Dim, IxDynImpl};

#[allow(unused)]
use num_complex::Complex;

use rustmex_core::convert::{
	FromMatlabError,
};

use rustmex_core::{
	MatlabClass,
};

#[allow(unused)]
use rustmex_core::mappable::{Mappable, MutMappable};

use rustmex_core::pointers::MxArray;

#[allow(unused)]
use std::ffi::CString;

use rustmex_core::mxArray;

use rustmex_core::numeric::{NumericArray, MatlabNumber};
use crate::char::CharArray;
use crate::numeric::{Numeric, OwnedNumeric};

pub type FromMxError<'a> = FromMatlabError<&'a mxArray>;

/**
 * Trait describing how a type can be built from an mxArray. It takes in a shared
 * reference because Matlab still owns the data.
 */
pub trait FromMatlab<'a> {
	fn from_matlab(mx: &'a mxArray) -> Result<Self, FromMxError<'a>> where Self: Sized;
}

/**
 * Trait describing how a type can be moved into an mxArray. It takes ownership because
 * Matlab takes over memory management.
 */
pub trait ToMatlab {
	fn to_matlab(self) -> MxArray;
}

pub trait MatlabScalar: Copy + MatlabNumber {}
macro_rules! impl_mls {
	($t:ty) => {impl MatlabScalar for $t {} }
}

impl_mls!(u8);
impl_mls!(i8);
impl_mls!(i16);
impl_mls!(u16);
impl_mls!(u32);
impl_mls!(i32);
impl_mls!(u64);
impl_mls!(i64);
impl_mls!(bool);
impl_mls!(f64);
impl_mls!(f32);

pub trait IntoRust<'a, T> {
	/**
	 * `from_matlab` is to `to_rust` as `from` is to `into`: this method
	 * makes method chaining possible, and thus allows for greater ergonomics. Best
	 * used in combination with
	 * [`missing::error_if_missing`](crate::message::Missing::error_if_missing).
	 */
	fn to_rust(&self) -> Result<T, FromMxError<'a>>;
}

impl<'a, T> IntoRust<'a, T> for &'a mxArray where T: FromMatlab<'a> {
	fn to_rust(&self) -> Result<T, FromMxError<'a>> {
		T::from_matlab(self)
	}
}

/**
 * Implementation of the FromMatlab trait for scalar mxArrays.
 */
impl<'a, T> FromMatlab<'a> for T where
	T: MatlabScalar
{
	fn from_matlab(mx: &'a mxArray) -> Result<Self, FromMxError<'a>> {
		let num = Numeric::from_mx_array(mx)?;
		let data = num.data();
		match data.len() {
			1 => Ok(data[0]),
			_ => Err(FromMxError::new_badsize(mx))
		}
	}
}

impl<'a, T> FromMatlab<'a> for ArrayViewD<'a, T> where
	Self: From<Numeric<T, &'a mxArray>>,
	T: MatlabNumber,
{
	fn from_matlab(mx: &'a mxArray) -> Result<Self, FromMxError<'a>> {
		Ok(Self::from(Numeric::from_mx_array(mx)?))
	}
}

/**
 * In Matlab, the empty array is often used as a sentinel object — there is no value to
 * be provided. This maps directly onto Rust's Option type, for which this implementation
 * is provided. Option's methods can thus be used to provide defaults or otherwise
 * operate on this maybe value.
 */
impl<'a, T> FromMatlab<'a> for Option<T> where T: FromMatlab<'a> {
	fn from_matlab(mx: &'a mxArray) -> Result<Self, FromMxError<'a>> {
		Ok(if mx.numel() == 0 {
			None
		} else {
			Some(T::from_matlab(mx)?)
		})
	}
}

/**
 * Convert an empty MxArray of some type into an empty Rust array.
 *
 * This will check whether the type of the mxArray matches the requested type. If it
 * does, it will construct an empty array of that type and return it.
 *
 * Note that using this with `Option<T>::from_matlab` will return an unconditional `None`,
 * assuming the types match.
 */
impl<'a, T> FromMatlab<'a> for [T; 0] where
	T: MatlabNumber
{
	fn from_matlab(mx: &'a mxArray) -> Result<Self, FromMxError<'a>> {
		let num = Numeric::<T, &'a mxArray>::from_mx_array(mx)?;

		if num.is_empty() { Ok([]) } else { Err(FromMxError::new_badsize(mx)) }
	}
}

/**
 * Convert an empty mxArray into an empty Rust array.
 *
 * This ignores the type of the mxArray; if it is empty it will construct an empty tuple
 * and return it.
 *
 * If you just want to know whether an array is empty, consider calling
 * [`mxArray::is_empty`] instead.
 *
 * Note that using this with `Option<T>::from_matlab` returns an unconditional `None`.
 */
impl<'a> FromMatlab<'a> for () {
	fn from_matlab(mx: &'a mxArray) -> Result<Self, FromMxError<'a>> {
		if mx.is_empty() { Ok(()) } else { Err(FromMxError::new_badsize(mx)) }
	}
}

/**
 * Convert an owned Array into an mxArray, transferring onwership to it.
 */
impl<'a, T, D> ToMatlab for Array<T, D> where
	D: Dimension,
	Numeric<T, MxArray>: NumericArray<'a>,
	T: MatlabNumber,
	Self: 'a,
{
	fn to_matlab(self) -> MxArray {
		Numeric::from(self).into_inner()
	}
}

/// Allocate a scalar on the heap and place it in an [`MxArray`]
impl<T> ToMatlab for T where T: MatlabScalar {
	fn to_matlab(self) -> MxArray {
		Numeric::from(self).into_inner()
	}
}

// TODO: Re-add FromMatlab implementations for complex arrays

/**
 * Copy a character vector into a CString.
 */
// TODO: Re-add support for directly getting UTF-8 strings
impl<'a> FromMatlab<'a> for CString {
	fn from_matlab(mx: &'a mxArray) -> Result<Self, FromMxError<'a>> {
		Ok(CharArray::from_mx_array(mx)?.get_cstring())
	}
}

/**
 * Convert the empty tuple into an empty `f64` `MxArray` array.
 *
 * This mirrors Matlab; the empty array ("`[]`") has the `double` type.
 */
impl ToMatlab for () {
	fn to_matlab(self) -> MxArray {
		OwnedNumeric::from(()).into_inner()
	}
}

/**
 * Convert an empty array into an empty `MxArray`.
 */
impl<T: MatlabNumber> ToMatlab for [T; 0] {
	fn to_matlab(self) -> MxArray {
		OwnedNumeric::<T>::from([]).into_inner()
	}
}