rustmex 0.6.4

Rustmex: providing convenient Rust bindings to Matlab MEX API's
Documentation
/*!
 * Rustmex: A library providing convenient Rust bindings to Matlab's MEX C api.
 *
 * Rustmex makes writing MEX functions in Rust a bit easier. It convert Matlab types, and
 * the arguments it provides to mexFunction into more Rusty types, which can then be used
 * to interface with other Rust code easily.
 *
 * For installation instructions, see the [`backend`] module.
 */

#[cfg(feature = "matlab800")]
pub mod backend {
	#![doc = include_str!("backend_installation.md")]
	#[cfg(any(feature="matlab700", feature="octave"))]
	compile_error!("Pick only one backend");
	extern crate rustmex_matlab800;
}

#[cfg(feature = "matlab700")]
pub mod backend {
	#![doc = include_str!("backend_installation.md")]
	#[cfg(any(feature="matlab800", feature="octave"))]
	compile_error!("Pick only one backend");
	extern crate rustmex_matlab700;
}

#[cfg(feature = "octave")]
pub mod backend {
	#![doc = include_str!("backend_installation.md")]
	#[cfg(any(feature="matlab700", feature="matlab800"))]
	compile_error!("Pick only one backend");
	extern crate rustmex_octave;
}

#[cfg(all(doc, not(any(feature="matlab800", feature="matlab700", feature="octave"))))]
pub mod backend {
	#![doc = include_str!("backend_installation.md")]
}

pub mod convert;
pub mod message;
pub mod function;
pub mod workspace;

pub mod alloc;
pub mod structs;
pub mod index;

pub mod objects;
pub mod cell;
pub mod numeric;
pub mod char;

// as to not have to dig it out of mex or num_complex every time
pub use rustmex_core::convert::{FromMatlabError};
pub use ::num_complex::Complex;
pub use message::{MexMessage, Error, Missing};
pub use rustmex_core::{mxArray, pointers::MxArray};
pub use rustmex_core::{
	MatlabClass,
	MutMatlabClass,
	NewEmpty,
	OwnedMatlabClass,
	TakeData,
};

/// The "left hand side" of a call to a mex function. In this slice, the return values
/// should be placed.
///
/// If you want to return a value in `ans`, even when `nargout`/`nlhs` are zero, use the
/// [`LhsAns`] type inplace of `Lhs` in the function annotated with
/// `#[rustmex::entrypoint]`.
pub type Lhs<'mex> = &'mex mut [Option<MxArray>];

/// The "Right hand side" of a call to a mex function. These are the arguments provided to
/// it in matlab.
///
/// the mxArray here lives for as long as "matlab" "does", since returning it to matlab
/// ensures that it lives for as long as needed --- matlab takes responsibility for
/// managing its memory.
pub type Rhs<'mex, 'matlab> = &'mex [&'matlab mxArray];

/// Re-export the macro to annotate the entry point.
///
/// Each MEX function file has an entrypoint called `mexFunction`. This is a C FFI function,
/// which, preferably, you do not want to write yourself.
///
/// Instead, rustmex provides the entrypoint macro; a macro to mark your Rust entrypoint
/// with. For example:
///
/// ```rust,ignore
/// use rustmex::prelude::*;
///
/// #[rustmex::entrypoint]
/// fn hello_world(lhs: Lhs, rhs: Rhs) -> rustmex::Result<()> {
/// 	println!("Hello Matlab!");
/// 	Ok(())
/// }
/// ```
#[cfg(feature = "entrypoint")]
pub use rustmex_entrypoint::entrypoint;

/**
 * Convenience type for returning a Error containing a MexMessage in the error path.
 *
 * Generating a Matlab error diverges; it does not return to Rust code. This prevents
 * destructors from running, and may thus leak memory. Returning from the entrypoint with
 * this type's `Err` variant is therefore preferable.
 */
pub type Result<T> = std::result::Result<T, message::Error>;

/**
 * As is convention in Rust, Rustmex defines a prelude to easily import often used
 * functionality.
 */
pub mod prelude {
	// Since this is a prelude, it only re-exports other things. It should be kept as
	// small as possible. If new items are added, add a short description as to why
	// these items are important enough to be included in the prelude.
	pub use super::{
		// Easy definition of the entrypoint. Do not add message::Error, or
		// crate::Result, since they might conflict with the standard library
		// types.
		Lhs, Rhs, LhsAns,

		// Easy argument parsing
		Missing,

		// Traits to allow for direct easy conversions into data types, a pretty
		// important part of the library. While, e.g. functions are nice, not
		// everyone wants to use them. Everyone runs into these.
		//convert::{FromMatlab, ToMatlab, VecToMatlab, VecType},
	};
	// These are the basically the core of the api
	pub use ::rustmex_core::{mxArray, pointers::MxArray};
}

use std::fmt::Display;

/**
 * Generate an ad-hoc warning from an id and a message
 *
 * See also [message::warning].
 */
pub fn warning<Id, Msg>(id: Id, msg: Msg) -> ()
where
	Id: AsRef<str>,
	Msg: Display
{
	message::warning(&message::AdHoc(id, msg))
}

/**
 * Check some boolean condition; if it is not met, return an Err(Error) with the given id
 * and message. Can be used within functions which return a
 * [`rustmex::Result`](Result).
 */
#[macro_export]
macro_rules! assert {
	($condition:expr, $id:expr, $msg:expr) => {
		::rustmex::error_on!(!$condition, $id, $msg);
	}
}

/**
 * Check some boolean condition, if it is met, return an Err(Error) with the given id and
 * message. Can be used within functions which return a [`rustmex::Result`](Result).
 */
#[macro_export]
macro_rules! error_on {
	($cond:expr, $id:expr, $msg:expr) => {
		if $cond {
			::rustmex::error!($id, $msg);
		}
	}
}

/**
 * Unconditionally return a rustmex::Result with the error variant, set with an id and a
 * message. Useful when the error condition has already been checked in some other way.
 */
#[macro_export]
macro_rules! error {
	($id:expr, $msg:expr) => {
		return Err(::rustmex::message::AdHoc($id, $msg).into());
	}
}


/**
 * Represents the return values of the Mex function.
 *
 * Matlab functions, and Mex functions, can return multiple return values. The amount of
 * return values requested is given in `nargout`/`nlhs`. However, [there is always
 * space](https://www.mathworks.com/help/matlab/apiref/mexfunction.html)
 * to return an `ans` result (the first return value).
 *
 * This type represents that behaviour. It acts similarly to [`Lhs`], but if `nlhs = 0`
 * it still returns a slice of length one so that a return value for the `ans` variable
 * can be placed there.
 *
 * If you want the 'old' behaviour you can still use the [`Lhs`] type.
 */
#[repr(transparent)]
#[derive(Debug)]
pub struct LhsAns<'mex>(&'mex mut [Option<MxArray>]);

impl<'mex> LhsAns<'mex> {

	/**
	 * Return the amount of space available for return values.
	 */
	pub fn space(&self) -> usize {
		self.0.len().max(1)
	}

	/// Return the value of `nlhs`. This may differ from self.len() if `nlhs = 0`
	pub fn nlhs(&self) -> usize {
		self.0.len()
	}

	/**
	 * Return an array guaranteed to have space for the `ans` variable.
	 */
	fn ans_or_lhs(&self) -> Lhs<'mex> {
		use ::std::slice::from_raw_parts_mut;

		let len = self.space();
		let lhs = &self.0;

		let ptr = lhs.as_ptr();

		// SAFETY: If the caller upholds the contract of only passing in Matlab's
		// lhs slice it is safe to extend a possibly zero-length slice by one,
		// because the Matlab documentation specifies (or at least implies) that there
		// is always 1 slot available to pass the `ans` variable into,
		// even if `nlhs` is zero.
		unsafe { from_raw_parts_mut(ptr as *mut _, len as usize) }
	}

	// SAFETY: LhsAns is a type safe to use _if and only if_ the slice it was
	// constructed with is Matlab's Lhs slice, i.e. even if Matlab passes in
	// nlhs = 0, there is still space allocated to store _1_ MxArray.
	//
	// This type stores the [Option<MxArray>; nlhs] slice, so if `nlhs = 0` this type
	// contains an empty slice. This is because, given that the type is
	// repr(transparent), it can be transmuted without issue to a bare `Lhs` in the
	// entrypoint proc macro, which makes the two byte-compatible. If instead this
	// type would store the full slice with a flag whether nlhs was zero that
	// wouldn't be the case.
	#[doc(hidden)]
	pub unsafe fn new(slice: Lhs<'mex>) -> Self {
		Self(slice)
	}

	/// Returns the bare (immutable) [`Lhs`] slice. If `nlhs = 0`, this slice is
	/// empty.
	pub fn lhs(&self) -> &'_ [Option<MxArray>] {
		&*self.0
	}

	/// Returns the bare [`Lhs`] slice. If `nlhs = 0`, this slice is empty.
	pub fn lhs_mut(&mut self) -> Lhs<'_> {
		&mut *self.0
	}

	/// Get a reference to the `ans` slot. Given that it is always available, this
	/// omits the bounds check.
	pub fn ans(&self) -> &'_ Option<MxArray> {
		use core::ops::Deref;
		let ans = self.deref().first();
		// SAFETY: Documentation specifies that there will always be space to
		// write a variable which will be returned in the `ans` variable, so
		// doing an `unwrap_unchecked` should be okay.
		unsafe { ans.unwrap_unchecked() }
	}

	/// Get a mutable reference to the `ans` slot. Given that it is always available,
	/// this omits the bounds check.
	pub fn ans_mut(&mut self) -> &'_ mut Option<MxArray> {
		use core::ops::DerefMut;
		let ans = self.deref_mut().first_mut();
		// SAFETY: Documentation specifies that there will always be space to
		// write a variable which will be returned in the `ans` variable, so
		// doing an `unwrap_unchecked` should be okay.
		unsafe { ans.unwrap_unchecked() }
	}
}

impl<'mex> core::ops::Deref for LhsAns<'mex> {
	type Target = [Option<MxArray>];

	fn deref(&self) -> &Self::Target {
		let lhs = self.ans_or_lhs();
		&*lhs
	}
}

impl<'mex> core::ops::DerefMut for LhsAns<'mex> {
	fn deref_mut(&mut self) -> &mut Self::Target {
		self.ans_or_lhs()
	}
}