1use std::ffi::CString;
10
11use std::fmt::{self, Display};
12
13use ndarray::{ShapeError, ErrorKind};
14
15use rustmex_core::{
16 convert::{
17 FromMatlabError,
18 FromMatlabErrorReason,
19 },
20};
21
22use rustmex_core::mxArray;
23use rustmex_core::pointers::MxArray;
24
25pub trait MexMessage: Display {
33 fn id(&self) -> &str;
34}
35
36pub struct Error {
42 error_id: String,
43 error_msg: String,
44}
45
46impl Error {
47 pub fn id(&self) -> &str {
48 &self.error_id
49 }
50
51 pub fn to_string(self) -> String {
52 self.error_msg
53 }
54}
55
56impl<T> From<T> for Error where T: MexMessage {
57 fn from(other: T) -> Self {
58 Error { error_id: other.id().to_string(), error_msg: other.to_string()}
59 }
60}
61
62#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
63pub struct MissingVariable {
64 id: &'static str,
65 msg: &'static str,
66}
67
68pub trait Missing<T> {
83 fn error_if_missing(self, id: &'static str , msg: &'static str)
84 -> Result<T, MissingVariable> where Self: Sized;
85}
86
87impl<'a> Missing<&'a mxArray> for Option<&'_ &'a mxArray> {
95 fn error_if_missing(self, id: &'static str, msg: &'static str)
96 -> Result<&'a mxArray, MissingVariable>
97 {
98 match self {
99 Some(v) => Ok(*v),
100 None => Err(MissingVariable {
101 id, msg
102 })
103 }
104 }
105}
106
107impl<'s> Missing<&'s mut Option<MxArray>> for Option<&'s mut Option<MxArray>> {
117 fn error_if_missing(self, id: &'static str, msg: &'static str)
118 -> Result<&'s mut Option<MxArray>, MissingVariable>
119 {
120 match self {
121 Some(v) => Ok(v),
122 None => Err(MissingVariable {
123 id, msg
124 })
125 }
126 }
127}
128
129impl MexMessage for MissingVariable {
130 fn id(&self) -> &str {
131 self.id
132 }
133}
134
135impl Display for MissingVariable {
136 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137 write!(f, "{}", self.msg)
138 }
139}
140
141impl<S> MexMessage for FromMatlabError<S> {
142 fn id(&self) -> &str {
143 match self.reason() {
144 FromMatlabErrorReason::BadClass => "rustmex:convert:bad_class",
145 FromMatlabErrorReason::BadComplexity => "rustmex:convert:bad_complexity",
146 FromMatlabErrorReason::BadSparsity => "rustmex:convert:bad_sparsity",
147 FromMatlabErrorReason::Size => "rustmex:convert:size_mismatch",
148 _ => "rustmex:enum_defined_elsewhere"
150 }
151 }
152}
153
154pub struct AdHoc<Id, Msg>(pub Id, pub Msg);
155
156impl<Id, Msg> MexMessage for AdHoc<Id, Msg> where Id: AsRef<str>, Msg: Display {
161 fn id(&self) -> &str { self.0.as_ref() }
162}
163
164impl<Id, Msg> Display for AdHoc<Id, Msg> where Msg: Display {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.1.fmt(f) }
166}
167
168pub fn warning(w: &dyn MexMessage) -> () {
172 let id = CString::new(w.id()).expect("No inner nul bytes");
173 let msg = CString::new(w.to_string()).expect("No inner nul bytes");
174 unsafe { rustmex_core::shim::rustmex_warn_id_and_txt(id.as_ptr(), msg.as_ptr()); }
175}
176
177#[macro_export]
191macro_rules! trigger_error {
192 ($e:expr) => {{
193 use ::rustmex::{
194 message::MexMessage,
195 alloc::NonPersistent as NP,
196 };
197
198 let e = $e;
199 let id = unsafe { NP::from_stringish(e.id(), true) };
200 let msg = unsafe { NP::from_stringish(e.to_string(), true) };
201
202 unsafe { ::rustmex::message::trigger_error_fn(NP::ptr(&id) as *const i8, NP::ptr(&msg) as *const i8); }
206 }}
207}
208
209pub unsafe fn trigger_error_fn(id: *const i8, msg: *const i8) -> ! {
215 ::rustmex_core::shim::rustmex_err_id_and_txt(id, msg);
216 unreachable!()
217}
218
219impl MexMessage for ShapeError {
220 fn id(&self) -> &str {
221 use ErrorKind::*;
222 match self.kind() {
223 IncompatibleShape => "ndarray:incompatible_shape",
224 IncompatibleLayout => "ndarray:incompatible_layout",
225 RangeLimited => "ndarray:range_limited",
226 OutOfBounds => "ndarray:out_of_bounds",
227 Unsupported => "ndarray:unsupported",
228 Overflow => "ndarray:overflow",
229 _ => "rustmex:ndarray:unrecognised_errorkind",
230 }
231 }
232}
233
234use crate::structs::StructError;
235
236impl MexMessage for StructError {
237 fn id(&self) -> &str {
238 match self {
239 StructError::OutOfBounds => "rustmex:structs:out_of_bounds",
240 StructError::NotAField => "rustmex:structs:not_a_field",
241 }
242 }
243}
244
245impl Display for StructError {
246 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247 let s = match self {
248 StructError::OutOfBounds => "Index is out of bounds",
249 StructError::NotAField => "Field does not exist",
250 };
251 write!(f, "{}", s)
252 }
253}
254
255use crate::objects::ObjectError;
257
258impl MexMessage for ObjectError {
259 fn id(&self) -> &str {
260 match self {
261 ObjectError::OutOfBounds => "rustmex:objects:out_of_bounds",
262 ObjectError::NotAccessible => "rustmex:objects:not_accessible",
263 }
264 }
265}
266
267impl Display for ObjectError {
268 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269 let s = match self {
270 ObjectError::OutOfBounds => "Index is out of bounds",
271 ObjectError::NotAccessible => "Property is not accessible",
272 };
273 write!(f, "{}", s)
274 }
275}