rustmex 0.6.4

Rustmex: providing convenient Rust bindings to Matlab MEX API's
Documentation
/*!
 * Workspace related functions
 *
 * Matlab has several functions in the MEX API which deal with getting variables from,
 * and putting them into workspaces. This module wraps that functionality.
 */

use std::ffi::CStr;

use crate::{
	mxArray,
	MxArray,

};

use rustmex_core::shim::{
	rustmex_put_variable as mexPutVariable,
	rustmex_get_variable_ptr as mexGetVariablePtr,
};

/**
 * Possible workspaces to put your variable
 */
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum WorkSpace {
	/// The base workspace, i.e. the command prompt.
	Base,
	/// The workspace formed by the callframe of the function that called this MEX
	/// function. Equivalent to [`Base`](WorkSpace::Base) if called from the command prompt.
	Caller,
	/// The workspace with all the global variables.
	Global
}

impl WorkSpace {
	/// Convert a [`WorkSpace`] into a [`CStr`] for passing into Matlab.
	const fn as_cstr(&self) -> &CStr {
		use WorkSpace::*;

		// SAFETY: Trivially evident that the strings end with a nul byte
		match self {
			Base => unsafe { CStr::from_bytes_with_nul_unchecked(b"base\0") },
			Caller => unsafe { CStr::from_bytes_with_nul_unchecked(b"caller\0") },
			Global => unsafe { CStr::from_bytes_with_nul_unchecked(b"global\0") }
		}
	}

	/// Get the WorkSpace as a constant char pointer.
	const fn as_ptr(&self) -> *const i8 {
		self.as_cstr().as_ptr()
	}
}

/**
 * Copy a variable out of a specified workspace. Returns `None` if the variable does not
 * exist in that workspace.
 *
 * Note that this function copies, in contrast to [`get_variable_ref`]. It is safer,
 * since there is no lifetime to deal with, but do not use this function for large
 * arrays.
 */
pub fn get_variable(ws: WorkSpace, name: &CStr) -> Option<MxArray> {
	// In some APIs, there is a function called `mexGetVariable`, which does what
	// this function is supposed to do. However, the matlab_separated bindings do not
	// have that function. They do have something similar, but there is no
	// documentation to be found. Implementing this function in this way is therefore
	// hedging against assuming that that function does the same as `mexGetVariable`..
	// It does not really matter on which side of the FFI the duplication happens.
	unsafe { get_variable_ref(ws, name) }.map(|x| x.duplicate())
}

/**
 * Get a reference to a variable named `name` in a specified workspace. If not present,
 * this function returns `None`.
 *
 * ## Safety
 * This function is marked unsafe, since it returns an unbounded reference, as there is
 * currently no way to to bind that lifetime. The Matlab documentation specifies that
 * this reference should not outlive the current call to your MEX function; it is up to
 * you to enforce this constraint.
 */
// Lifetime elision would, without these explicit lifetimes, assume that the lifetime of
// the mxArray were bounded by the lifetime of the name CStr. This is not true, so we
// need to explicitly annotate it.
pub unsafe fn get_variable_ref<'str, 'var>(ws: WorkSpace, name: &'str CStr) -> Option<&'var mxArray> {
	let ptr = mexGetVariablePtr(ws.as_ptr(), name.as_ptr());
	if ptr.is_null() {
		None
	} else {
		Some(&*ptr)
	}
}

/**
 * Put an MxArray in a workspace with a specified name, overwriting when a variable with
 * that name already exists.
 *
 * Ownership of the variable is transferred to Matlab.
 */
pub fn put_variable(ws: WorkSpace, name: &CStr, var: MxArray) {
	let ret = unsafe { mexPutVariable(ws.as_ptr(), name.as_ptr(),
		MxArray::transfer_responsibility(var)) };

	if ret != 0 {
		panic!("There is no way to make an MxArray with a null pointer, yet MexPutVariable failed (per the docs: 'A possible cause of failure is that pm is NULL.')")
	}
}