rustmex 0.6.2

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 and documtoentation & linkage examples, refer to the documentation.

## v0.6 Update Notes
Rustmex v0.6 constitutes a major redesign of the crate. It has been broken up into a set
of crates, such that each crate is compilable separately; feature flags are no longer
required for Rustmex itself to compile. However, the easiest installation method is still
to use feature flags to select the appropriate backend; confer to the documentation of
the backend module for instructions. The 'normal' user facing API of Rustmex should
however still mostly be the same as before; only the internals have changed.

## Usage Examples
This section contains some examples of how Rustmex can be used. Note however that these
are not tested, since testing MEX functions involve starting a Matlab/Octave interpreter,
and so this cannot be done automatically. Future versions of Rustmex will come with
runnable examples.

That being said, 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(())
}
```

Note that this example mirrors the definition of `mexFunction` itself: Matlab has already
allocated a return value array, you just need to place your results into it.

The `FromMatlabError` is for when you want to convert an mxArray into a more Rusty data
type. These conversions are not infallible: Matlab is dynamically typed, and the provided
`mxArray` must have the same type and size as the type we want to convert it into.

As a more convoluted example:
```rust,ignore
use rustmex::prelude::*;

#[rustmex::entrypoint]
fn euclidean_length(lhs: Lhs, rhs: Rhs) -> rustmex::Result<()> {
	let v: &[f64] = rhs
		.get(0)
		.error_if_missing("euclidean_length:missing_input",
			"Missing input vector to compute the length of")?
		.data_slice()?;

	// I recommend to only execute your planned computation once you're sure it
	// actually needs to be computed — it's a bit of a waste to compute an expensive
	// result without somewhere to return it to.
	if let Some(ret) = lhs.get_mut(0) {
		ret.replace(v.iter().map(|x|x*x).sqrt().to_matlab());
	}
	Ok(())
}
```

This example computes the Euclidean length of an input vector. Note that type annotations
are (almost always) needed for the return type of `data_slice()` and `from_matlab()`.

Regarding [`FromMatlab`][`convert::FromMatlab`], this library also supports building
NDarrays from mxArrays. It will ensure that NDarray understands the data the same way
matlab does. For example, the following prints out a debug representation of the array:
```rust,ignore
use rustmex::prelude::*;
use ndarray::ArrayViewD;

#[rustmex::entrypoint]
fn display_matrix(lhs: Lhs, rhs: Rhs) -> rustmex::Result<()> {
	if let Some(mx) = rhs.get(0) {
		let mat = ArrayViewD<f64>::from_matlab(mx)?;
		eprintln!("{mat:#?}");
	}
	Ok(())
}
```

Calling back into Matlab is also supported. For example, to compute the square
root of the sum of squares (i.e. nd-pythagoras):
```matlab
% Call Rust MEX file
x = rusty_fn_call(@(x) sqrt(sum(x.^2)), 1:10)
```
```rust,ignore
use rustmex::prelude::*
use rustmex::function::Function;

#[rustmex::entrypoint]
fn rusty_fn_call(lhslice: rustmex::Lhs, rhslice: rustmex::Rhs) -> rustmex::Result<()> {

	let f = rhslice
		.get(0)
		.error_if_missing("rusthello:no_fn", "Didn't get a function")?
		.to_rust::<Function<_>>()?;

	if let Some(r) = lhslice.get_mut(0) {
		// Just forward the remaining arguments
		let mut results: Box<[Option<MxArray>; 1]> = f.call(&rhslice[1..]).unwrap();

		let v = results[0].take().unwrap();

		r.replace(v);
	};

	return Ok(());
}
```
prints:
```text
x =  19.621
```

If you assume something about the data you receive, but it might not yet be in the right
shape, you might want to reshape. Rustmex enables ergonomically reshaping data: you can
apply the `?` operator on a `Result` with a `ShapeError` in functions which return a
`rustmex::Result`:
```rust,ignore
use rustmex::prelude::*;
use ndarray::{ArrayViewD, Ix2};

#[rustmex::entrypoint]
fn display_matrix(lhs: Lhs, rhs: Rhs) -> rustmex::Result<()> {
	if let Some(mx) = rhs.get(0) {
		let mat = ArrayViewD<f64>::from_matlab(mx)?
			.into_dimensionality<Ix2>()?
			.into_shape((1, 4))?;
		eprintln!("{mat:#?}");
	}
	Ok(())
}
```

## Licence
This is licensed to you under the Mozilla Public License, version 2. You can the licence
in the LICENCE file in this project's source tree root folder.

## Authors
- Niels ter Meer (maintainer)