use std::ffi::CString;
use std::fmt::{self, Display};
use ndarray::{ShapeError, ErrorKind};
use rustmex_core::{
convert::{
FromMatlabError,
FromMatlabErrorReason,
},
};
use rustmex_core::mxArray;
use rustmex_core::pointers::MxArray;
pub trait MexMessage: Display {
fn id(&self) -> &str;
}
pub struct Error {
error_id: String,
error_msg: String,
}
impl Error {
pub fn id(&self) -> &str {
&self.error_id
}
pub fn to_string(self) -> String {
self.error_msg
}
}
impl<T> From<T> for Error where T: MexMessage {
fn from(other: T) -> Self {
Error { error_id: other.id().to_string(), error_msg: other.to_string()}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct MissingVariable {
id: &'static str,
msg: &'static str,
}
pub trait Missing<T> {
fn error_if_missing(self, id: &'static str , msg: &'static str)
-> Result<T, MissingVariable> where Self: Sized;
}
impl<'a> Missing<&'a mxArray> for Option<&'_ &'a mxArray> {
fn error_if_missing(self, id: &'static str, msg: &'static str)
-> Result<&'a mxArray, MissingVariable>
{
match self {
Some(v) => Ok(*v),
None => Err(MissingVariable {
id, msg
})
}
}
}
impl<'s> Missing<&'s mut Option<MxArray>> for Option<&'s mut Option<MxArray>> {
fn error_if_missing(self, id: &'static str, msg: &'static str)
-> Result<&'s mut Option<MxArray>, MissingVariable>
{
match self {
Some(v) => Ok(v),
None => Err(MissingVariable {
id, msg
})
}
}
}
impl MexMessage for MissingVariable {
fn id(&self) -> &str {
self.id
}
}
impl Display for MissingVariable {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.msg)
}
}
impl<S> MexMessage for FromMatlabError<S> {
fn id(&self) -> &str {
match self.reason() {
FromMatlabErrorReason::BadClass => "rustmex:convert:bad_class",
FromMatlabErrorReason::BadComplexity => "rustmex:convert:bad_complexity",
FromMatlabErrorReason::BadSparsity => "rustmex:convert:bad_sparsity",
FromMatlabErrorReason::Size => "rustmex:convert:size_mismatch",
_ => "rustmex:enum_defined_elsewhere"
}
}
}
pub struct AdHoc<Id, Msg>(pub Id, pub Msg);
impl<Id, Msg> MexMessage for AdHoc<Id, Msg> where Id: AsRef<str>, Msg: Display {
fn id(&self) -> &str { self.0.as_ref() }
}
impl<Id, Msg> Display for AdHoc<Id, Msg> where Msg: Display {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.1.fmt(f) }
}
pub fn warning(w: &dyn MexMessage) -> () {
let id = CString::new(w.id()).expect("No inner nul bytes");
let msg = CString::new(w.to_string()).expect("No inner nul bytes");
unsafe { rustmex_core::shim::rustmex_warn_id_and_txt(id.as_ptr(), msg.as_ptr()); }
}
#[macro_export]
macro_rules! trigger_error {
($e:expr) => {{
use ::rustmex::{
message::MexMessage,
alloc::NonPersistent as NP,
};
let e = $e;
let id = unsafe { NP::from_stringish(e.id(), true) };
let msg = unsafe { NP::from_stringish(e.to_string(), true) };
unsafe { ::rustmex::message::trigger_error_fn(NP::ptr(&id) as *const i8, NP::ptr(&msg) as *const i8); }
}}
}
pub unsafe fn trigger_error_fn(id: *const i8, msg: *const i8) -> ! {
::rustmex_core::shim::rustmex_err_id_and_txt(id, msg);
unreachable!()
}
impl MexMessage for ShapeError {
fn id(&self) -> &str {
use ErrorKind::*;
match self.kind() {
IncompatibleShape => "ndarray:incompatible_shape",
IncompatibleLayout => "ndarray:incompatible_layout",
RangeLimited => "ndarray:range_limited",
OutOfBounds => "ndarray:out_of_bounds",
Unsupported => "ndarray:unsupported",
Overflow => "ndarray:overflow",
_ => "rustmex:ndarray:unrecognised_errorkind",
}
}
}
use crate::structs::StructError;
impl MexMessage for StructError {
fn id(&self) -> &str {
match self {
StructError::OutOfBounds => "rustmex:structs:out_of_bounds",
StructError::NotAField => "rustmex:structs:not_a_field",
}
}
}
impl Display for StructError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
StructError::OutOfBounds => "Index is out of bounds",
StructError::NotAField => "Field does not exist",
};
write!(f, "{}", s)
}
}
use crate::objects::ObjectError;
impl MexMessage for ObjectError {
fn id(&self) -> &str {
match self {
ObjectError::OutOfBounds => "rustmex:objects:out_of_bounds",
ObjectError::NotAccessible => "rustmex:objects:not_accessible",
}
}
}
impl Display for ObjectError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
ObjectError::OutOfBounds => "Index is out of bounds",
ObjectError::NotAccessible => "Property is not accessible",
};
write!(f, "{}", s)
}
}