#![deny(
missing_docs,
missing_debug_implementations,
missing_copy_implementations,
dead_code,
while_true
)]
use std::io::ErrorKind;
#[allow(unused)]
const XON: i8 = 17;
#[allow(unused)]
const XOFF: i8 = 19;
#[allow(unused)]
const CR: i8 = 13;
#[allow(unused)]
const LF: i8 = 10;
#[cfg(unix)]
pub mod posix;
#[cfg(windows)]
pub mod windows;
pub type SerialResult<T> = std::result::Result<T, SerialError>;
pub enum SerialError {
IoError(std::io::Error),
OsError {
code: u32,
desc: String,
},
LibraryError(String)
}
impl std::fmt::Debug for SerialError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::IoError(arg0) => f.debug_tuple("IoError").field(arg0).finish(),
Self::OsError { code, desc } => f
.debug_struct("OsError")
.field("code", code)
.field("desc", desc)
.finish(),
SerialError::LibraryError(e) => f.debug_tuple("LibraryError").field(e).finish(),
}
}
}
impl std::fmt::Display for SerialError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SerialError::IoError(e) => {
write!(f, "IoError {}", e)
}
SerialError::OsError { code, desc } => write!(f, "OsError {code} ({desc})"),
SerialError::LibraryError(e) => write!(f, "Serial-RS Lib error '{e}'"),
}
}
}
impl std::error::Error for SerialError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
if let Self::IoError(e) = self {
Some(e)
} else {
None
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct SerialPortSettings {
baud_rate: u32,
byte_size: ByteSize,
parity: Parity,
stop_bits: StopBits,
read_timeout: Option<u128>,
flow_control: FlowControl,
write_timeout: Option<u128>,
inter_byte_timeout: Option<u128>,
blocking: bool
}
impl Default for SerialPortSettings {
fn default() -> Self {
Self {
baud_rate: 9600,
byte_size: ByteSize::Eight,
parity: Parity::None,
stop_bits: StopBits::One,
read_timeout: None,
write_timeout: None,
flow_control: FlowControl::None,
inter_byte_timeout: None,
blocking: true
}
}
}
#[allow(missing_docs)]
impl SerialPortSettings {
pub fn baud(mut self, baud: u32) -> Self {
self.baud_rate = baud;
self
}
pub fn read_timeout(mut self, timeout: Option<u128>) -> Self {
self.read_timeout = timeout;
self
}
pub fn byte_size(mut self, byte_size: ByteSize) -> Self {
self.byte_size = byte_size;
self
}
pub fn write_timeout(mut self, timeout: Option<u128>) -> Self {
self.write_timeout = timeout;
self
}
pub fn parity(mut self, parity: Parity) -> Self {
self.parity = parity;
self
}
pub fn stop_bits(mut self, stop_bits: StopBits) -> Self {
self.stop_bits = stop_bits;
self
}
pub fn set_flow_control(mut self, method: FlowControl) -> Self {
self.flow_control = method;
self
}
pub fn set_blocking(mut self, blocking: bool) -> Self {
self.blocking = blocking;
self
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum FlowControl {
None,
DsrDtr,
XonXoff,
RtsCts
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum ByteSize {
Five,
Six,
Seven,
Eight,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Parity {
None,
Even,
Odd,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum StopBits {
One,
OnePointFive,
Two,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct PortInfo {
port: String,
hwid: String,
vid: u16,
pid: u16,
manufacturer: String,
description: String,
}
impl PortInfo {
pub fn get_port(&self) -> &str { &self.port }
pub fn get_hwid(&self) -> &str { &self.hwid }
pub fn get_pid(&self) -> u16 { self.pid }
pub fn get_vid(&self) -> u16 { self.vid }
pub fn get_manufacturer(&self) -> &str { &self.manufacturer }
pub fn get_desc(&self) -> &str { &self.description }
}
pub trait SerialPort: Send + std::io::Write + std::io::Read {
fn reconfigure_port(&mut self) -> SerialResult<()>;
fn close(self) -> SerialResult<()>;
fn set_buffer_size(&mut self, rx_size: usize, tx_size: usize) -> SerialResult<()>;
fn set_output_flow_control(&self, enable: bool) -> SerialResult<()>;
fn set_data_terminal_ready(&mut self, enable: bool) -> SerialResult<()>;
fn set_request_to_send(&mut self, enable: bool) -> SerialResult<()>;
fn set_break_state(&mut self, enable: bool) -> SerialResult<()>;
fn read_clear_to_send(&self) -> SerialResult<bool>;
fn read_data_set_ready(&self) -> SerialResult<bool>;
fn read_ring_indicator(&self) -> SerialResult<bool>;
fn read_carrier_detect(&self) -> SerialResult<bool>;
fn bytes_to_read(&self) -> SerialResult<usize>;
fn bytes_to_write(&self) -> SerialResult<usize>;
fn get_path(&self) -> String;
fn try_clone(&mut self) -> SerialResult<Box<dyn SerialPort>>;
fn clear_input_buffer(&mut self) -> SerialResult<()>;
fn clear_output_buffer(&mut self) -> SerialResult<()>;
}
pub trait PortScanner {
fn list_devices(&mut self) -> SerialResult<Vec<PortInfo>>;
}
impl From<SerialError> for std::io::Error {
fn from(e: SerialError) -> Self {
match e {
SerialError::IoError(i) => i,
SerialError::OsError { code: _ , desc } => std::io::Error::new(ErrorKind::Other, desc),
SerialError::LibraryError(e) => std::io::Error::new(ErrorKind::Other, e),
}
}
}
pub fn new(info: PortInfo, settings: Option<SerialPortSettings>) -> SerialResult<Box<dyn SerialPort>> {
#[cfg(unix)]
{
use posix::*;
Ok(Box::new(TTYPort::new(info.port, settings)?))
}
#[cfg(windows)]
{
use windows::*;
Ok(Box::new(COMPort::new(info.port, settings)?))
}
}
pub fn new_from_path(path: &str, settings: Option<SerialPortSettings>) -> SerialResult<Box<dyn SerialPort>> {
#[cfg(unix)]
{
use posix::*;
Ok(Box::new(TTYPort::new(path.to_string(), settings)?))
}
#[cfg(windows)]
{
use windows::*;
Ok(Box::new(COMPort::new(path.to_string(), settings)?))
}
}
pub fn list_ports() -> SerialResult<Vec<PortInfo>> {
#[cfg(unix)]
{
use posix::port_lister::TTYPortScanner;
TTYPortScanner{}.list_devices()
}
#[cfg(windows)]
{
use windows::port_lister::COMPortLister;
COMPortLister{}.list_devices()
}
}