From 5fac5c8da9108f4124217a8e42750b0347f18898 Mon Sep 17 00:00:00 2001
From: Federico Lolli <federico.lolli@skywarder.eu>
Date: Mon, 19 Feb 2024 00:24:27 +0100
Subject: [PATCH] building main

---
 src/error.rs |  71 ++++++++++++++++++++++++++++++++++
 src/lib.rs   |  31 +++++++++++++++
 src/main.rs  |   3 --
 src/types.rs | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 207 insertions(+), 3 deletions(-)
 create mode 100644 src/error.rs
 create mode 100644 src/lib.rs
 delete mode 100644 src/main.rs
 create mode 100644 src/types.rs

diff --git a/src/error.rs b/src/error.rs
new file mode 100644
index 0000000..29a57c7
--- /dev/null
+++ b/src/error.rs
@@ -0,0 +1,71 @@
+use rustmex::{message::AdHoc, mxArray, FromMatlabError};
+
+pub trait MapMexError<T> {
+    fn map_err_adhoc(self, id: &str, msg: &str) -> Result<T, rustmex::Error>;
+    fn mexerr(self, err: Error) -> Result<T, rustmex::Error>;
+}
+
+impl<T, E> MapMexError<T> for Result<T, E> {
+    fn map_err_adhoc(self, id: &str, msg: &str) -> Result<T, rustmex::Error> {
+        Ok(self.map_err(|_| AdHoc(id, msg))?)
+    }
+
+    fn mexerr(self, err: Error) -> Result<T, rustmex::Error> {
+        self.map_err(|_| err.to_mexerr())
+    }
+}
+
+impl<T> MapMexError<T> for Option<T> {
+    fn map_err_adhoc(self, id: &str, msg: &str) -> Result<T, rustmex::Error> {
+        Ok(self.ok_or(AdHoc(id, msg))?)
+    }
+
+    fn mexerr(self, err: Error) -> Result<T, rustmex::Error> {
+        self.ok_or_else(|| err.to_mexerr())
+    }
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum Error {
+    #[error("Missing serial mode (first argument)")]
+    MissingSerialMode,
+    #[error("Missing port name (second argument)")]
+    MissingPortName,
+    #[error("Missing baudrate (third argument)")]
+    MissingBaudrate,
+    #[error("String contains invalid characters")]
+    String(#[from] std::ffi::IntoStringError),
+    #[error("Invalid serial mode")]
+    InvalidMode,
+    #[error("{0}")]
+    Rustmex(#[from] rustmex::Error),
+}
+
+impl Error {
+    fn id(&self) -> &str {
+        match self {
+            Error::MissingSerialMode => "serialbridge:missing_input",
+            Error::MissingPortName => "serialbridge:missing_input",
+            Error::MissingBaudrate => "serialbridge:missing_input",
+            Error::String(_) => "serialbridge:invalid_input",
+            Error::InvalidMode => "serialbridge:invalid_input",
+            Error::Rustmex(err) => err.id(),
+        }
+    }
+
+    pub fn to_mexerr(&self) -> rustmex::Error {
+        AdHoc(self.id(), self.to_string()).into()
+    }
+}
+
+impl From<Error> for rustmex::Error {
+    fn from(err: Error) -> Self {
+        err.to_mexerr()
+    }
+}
+
+impl From<FromMatlabError<&mxArray>> for Error {
+    fn from(err: FromMatlabError<&mxArray>) -> Self {
+        err.into()
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..9badc03
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,31 @@
+//! WARNING: panic is used as stated in the example at
+//! [rustmex](https://gitlab.com/nielstermeer/rustmex/-/blob/master/examples/catch_panic/src/lib.rs?ref_type=heads).
+
+mod error;
+mod types;
+
+use rustmex::{char::CharArray, prelude::*, MatlabClass};
+
+use error::{Error, MapMexError};
+
+#[rustmex::entrypoint]
+fn serialbridge(lhs: Lhs, rhs: Rhs) -> rustmex::Result<()> {
+    let arg0 = rhs.first().mexerr(Error::MissingSerialMode)?;
+
+    // Get the mode argument ("Open", "Close", "Read", "Write")
+    let mode = get_mode(arg0);
+
+    let arg1 = rhs.get(1).error_if_missing(
+        "serialbridge:missing_input",
+        "Missing serial mode (second argument)",
+    )?;
+
+    Ok(())
+}
+
+fn get_mode(arg: &mxArray) -> Result<types::Mode, Error> {
+    CharArray::from_mx_array(arg)?
+        .get_cstring()
+        .into_string()?
+        .parse()
+}
diff --git a/src/main.rs b/src/main.rs
deleted file mode 100644
index e7a11a9..0000000
--- a/src/main.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-fn main() {
-    println!("Hello, world!");
-}
diff --git a/src/types.rs b/src/types.rs
new file mode 100644
index 0000000..1e58747
--- /dev/null
+++ b/src/types.rs
@@ -0,0 +1,105 @@
+use std::{
+    ffi::{CStr, CString},
+    ops::Deref,
+    str::FromStr,
+};
+
+use cstr::cstr;
+use rustmex::{
+    convert::ToMatlab,
+    error,
+    numeric::{Numeric, NumericArray},
+    structs::{ScalarStruct, Struct},
+    MatlabClass, MxArray,
+};
+
+use crate::error::Error;
+
+#[derive(Debug, Clone)]
+pub struct Serial(ScalarStruct<MxArray>);
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Mode {
+    Open,
+    Close,
+    Read,
+    Write,
+}
+
+impl FromStr for Mode {
+    type Err = crate::Error;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "Open" => Ok(Mode::Open),
+            "Close" => Ok(Mode::Close),
+            "Read" => Ok(Mode::Read),
+            "Write" => Ok(Mode::Write),
+            _ => Err(Error::InvalidMode),
+        }
+    }
+}
+
+// #[derive(Debug, Clone)]
+// struct ByteString(MxArray);
+
+// impl From<&str> for ByteString {
+//     fn from(value: &str) -> Self {
+//         let raw = value.as_bytes();
+//         Self(
+//             Numeric::new(raw.into(), &[1, raw.len()])
+//                 .expect("Failed to create byte string")
+//                 .into_inner(),
+//         )
+//     }
+// }
+
+// impl ByteString {
+//     fn from_string(value: String) -> Self {
+//         value.as_str().into()
+//     }
+
+//     fn into_string(self) -> rustmex::Result<String> {
+//         let value = Numeric::<u8, MxArray>::from_mx_array(self.0)?.data();
+//         Ok(String::from_utf8(value.to_vec()).expect("Failed to convert byte string to string"))
+//     }
+// }
+
+// impl Deref for ByteString {
+//     type Target = MxArray;
+
+//     fn deref(&self) -> &Self::Target {
+//         &self.0
+//     }
+// }
+
+// impl Serial {
+//     pub fn new(device: String, baudrate: u32) -> Self {
+//         let device_f = cstr!("serialport");
+//         let baud_f = cstr!("baudrate");
+//         let mut s = Struct::new(&[1, 1], &[device_f, baud_f])
+//             .into_scalar()
+//             .unwrap();
+//         // let device = CString::new(device.into()).unwrap();
+//         s.set(device_f, *ByteString::from_string(device)).unwrap();
+//         s.set(baud_f, baudrate.to_matlab()).unwrap();
+//         Serial(s)
+//     }
+
+//     pub fn into_struct(self) -> ScalarStruct<MxArray> {
+//         self.0
+//     }
+
+//     pub fn from_struct(s: ScalarStruct<MxArray>) -> Self {
+//         Serial(s)
+//     }
+
+// pub fn device(&self) -> rustmex::Result<String> {
+//     let device_f = cstr!("serialport");
+//     let a = self
+//         .0
+//         .get(device_f)
+//         .map(|x| x.ok_or_else(|| error!("serialbridge:missing_input", "Device not found")))?
+//         .map(|x| ByteString(x.to_owned()));
+// }
+// }
-- 
GitLab