mod close;
mod constructor;
mod flush;
mod input_waiting;
mod output_waiting;
mod poll;
mod read;
mod write;
#[cfg(test)]
pub mod integration_tests;
use mlua::Error::RuntimeError;
use mlua::{Error, Lua, Number, Table, UserData, UserDataFields, UserDataMethods};
use serial2::{CharSize, FlowControl, IntoSettings, Parity, SerialPort, Settings, StopBits};
use std::sync::{Arc, Mutex};
#[derive(Copy, Clone)]
#[allow(dead_code)]
enum ErrorCode {
Arg = -1,
Open = -2,
Query = -3,
Configure = -4,
Io = -5,
Close = -6,
}
#[derive(Clone)]
struct Config {
baudrate: u32,
char_size: CharSize,
flow_control: FlowControl,
parity: Parity,
stopbits: StopBits,
}
#[derive(Clone)]
struct Serial {
#[allow(dead_code)]
device: String,
port: Arc<Mutex<SerialPort>>,
}
impl IntoSettings for Config {
fn apply_to_settings(self, settings: &mut Settings) -> std::io::Result<()> {
let _ = settings.set_baud_rate(self.baudrate);
settings.set_parity(self.parity);
settings.set_char_size(self.char_size);
settings.set_flow_control(self.flow_control);
settings.set_stop_bits(self.stopbits);
Ok(())
}
}
impl UserData for Serial {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("close", close::handle);
methods.add_method("flush", flush::handle);
methods.add_method("input_waiting", input_waiting::handle);
methods.add_method("output_waiting", output_waiting::handle);
methods.add_method("poll", poll::handle);
methods.add_method("read", read::handle);
methods.add_method("write", write::handle);
}
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("baudrate", |_, serial| {
let baud_rate = {
let locked_port = serial.port.lock().map_err(|err| RuntimeError(err.to_string()))?;
locked_port.get_configuration()?.get_baud_rate()?
};
Ok(baud_rate)
});
fields.add_field_method_get("databits", |_, serial| {
let char_size = {
let locked_port = serial.port.lock().map_err(|err| RuntimeError(err.to_string()))?;
locked_port.get_configuration()?.get_char_size()?
};
Ok(match char_size {
CharSize::Bits5 => 5,
CharSize::Bits6 => 6,
CharSize::Bits7 => 7,
CharSize::Bits8 => 8,
})
});
#[cfg(target_family = "unix")]
fields.add_field_method_get("fd", |_, serial| {
use std::os::fd::AsRawFd;
let locked_port = serial.port.lock().map_err(|err| RuntimeError(err.to_string()))?;
Ok(locked_port.as_raw_fd())
});
#[cfg(target_family = "windows")]
fields.add_field_method_get("fd", |_, _serial| {
Err::<u64, mlua::Error>(RuntimeError("Unavailable on this platform".to_string()))
});
fields.add_field_method_get("parity", |_, serial| {
let parity = {
let locked_port = serial.port.lock().map_err(|err| RuntimeError(err.to_string()))?;
locked_port.get_configuration()?.get_parity()?
};
Ok(match parity {
Parity::Even => "even",
Parity::None => "none",
Parity::Odd => "odd",
})
});
fields.add_field_method_get("rtscts", |_, serial| {
let flow_control = {
let locked_port = serial.port.lock().map_err(|err| RuntimeError(err.to_string()))?;
locked_port.get_configuration()?.get_flow_control()?
};
Ok(flow_control == FlowControl::RtsCts)
});
fields.add_field_method_get("stopbits", |_, serial| {
let stop_bits = {
let locked_port = serial.port.lock().map_err(|err| RuntimeError(err.to_string()))?;
locked_port.get_configuration()?.get_stop_bits()?
};
Ok(match stop_bits {
StopBits::One => Number::from(1),
StopBits::Two => Number::from(2),
})
});
fields.add_field_method_get("xonxoff", |_, serial| {
let flow_control = {
let locked_port = serial.port.lock().map_err(|err| RuntimeError(err.to_string()))?;
locked_port.get_configuration()?.get_flow_control()?
};
Ok(flow_control == FlowControl::XonXoff)
});
fields.add_field_method_set("vmin", |_, _serial, _value: Number| {
Err(RuntimeError("NIY".to_string())) });
fields.add_field_method_set("vtime", |_, _serial, _value: Number| {
Err(RuntimeError("NIY".to_string())) });
}
}
pub fn preload(lua: &Lua) -> Result<(), Error> {
let module = lua.create_table()?;
let metatable = lua.create_table()?;
metatable.raw_set("__call", lua.create_function(constructor::handle)?)?;
module.set_metatable(Some(metatable))?;
let globals = lua.globals();
let package: Table = globals.get("package")?;
let loaded: Table = package.get("loaded")?;
loaded.set("periphery.Serial", module)?;
Ok(())
}
#[cfg(test)]
mod tests {
use mlua::Lua;
use std::error::Error;
#[test]
fn preload() -> Result<(), Box<dyn Error>> {
let lua = Lua::new();
super::preload(&lua)?;
lua.load("local Serial = require('periphery.Serial')").exec()?;
Ok(())
}
}