#![deny(
missing_docs,
missing_debug_implementations,
missing_copy_implementations,
unused
)]
#![doc(test(attr(allow(unused_must_use))))]
use std::convert::From;
use std::error::Error as StdError;
use std::fmt;
use std::io;
use std::time::Duration;
#[cfg(unix)]
mod posix;
#[cfg(unix)]
pub use posix::{BreakDuration, TTYPort};
#[cfg(windows)]
mod windows;
#[cfg(windows)]
pub use windows::COMPort;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorKind {
NoDevice,
InvalidInput,
Unknown,
Io(io::ErrorKind),
}
#[derive(Debug)]
pub struct Error {
pub kind: ErrorKind,
pub description: String,
}
impl Error {
pub fn new<T: Into<String>>(kind: ErrorKind, description: T) -> Self {
Error {
kind,
description: description.into(),
}
}
pub fn kind(&self) -> ErrorKind {
self.kind
}
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> std::result::Result<(), fmt::Error> {
fmt.write_str(&self.description)
}
}
impl StdError for Error {
fn description(&self) -> &str {
&self.description
}
}
impl From<io::Error> for Error {
fn from(io_error: io::Error) -> Error {
Error::new(ErrorKind::Io(io_error.kind()), format!("{}", io_error))
}
}
impl From<Error> for io::Error {
fn from(error: Error) -> io::Error {
let kind = match error.kind {
ErrorKind::NoDevice => io::ErrorKind::NotFound,
ErrorKind::InvalidInput => io::ErrorKind::InvalidInput,
ErrorKind::Unknown => io::ErrorKind::Other,
ErrorKind::Io(kind) => kind,
};
io::Error::new(kind, error.description)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum DataBits {
Five,
Six,
Seven,
Eight,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Parity {
None,
Odd,
Even,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum StopBits {
One,
Two,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum FlowControl {
None,
Software,
Hardware,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ClearBuffer {
Input,
Output,
All,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SerialPortBuilder {
path: String,
baud_rate: u32,
data_bits: DataBits,
flow_control: FlowControl,
parity: Parity,
stop_bits: StopBits,
timeout: Duration,
}
impl SerialPortBuilder {
pub fn path<'a>(mut self, path: impl Into<std::borrow::Cow<'a, str>>) -> Self {
self.path = path.into().as_ref().to_owned();
self
}
pub fn baud_rate(mut self, baud_rate: u32) -> Self {
self.baud_rate = baud_rate;
self
}
pub fn data_bits(mut self, data_bits: DataBits) -> Self {
self.data_bits = data_bits;
self
}
pub fn flow_control(mut self, flow_control: FlowControl) -> Self {
self.flow_control = flow_control;
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 timeout(mut self, timeout: Duration) -> Self {
self.timeout = timeout;
self
}
pub fn open(self) -> Result<Box<dyn SerialPort>> {
#[cfg(unix)]
return posix::TTYPort::open(&self).map(|p| Box::new(p) as Box<dyn SerialPort>);
#[cfg(windows)]
return windows::COMPort::open(&self).map(|p| Box::new(p) as Box<dyn SerialPort>);
#[cfg(not(any(unix, windows)))]
Err(Error::new(
ErrorKind::Unknown,
"open() not implemented for platform",
))
}
#[cfg(unix)]
pub fn open_native(self) -> Result<TTYPort> {
posix::TTYPort::open(&self)
}
#[cfg(windows)]
pub fn open_native(self) -> Result<COMPort> {
windows::COMPort::open(&self)
}
}
pub trait SerialPort: Send + io::Read + io::Write {
fn name(&self) -> Option<String>;
fn baud_rate(&self) -> Result<u32>;
fn data_bits(&self) -> Result<DataBits>;
fn flow_control(&self) -> Result<FlowControl>;
fn parity(&self) -> Result<Parity>;
fn stop_bits(&self) -> Result<StopBits>;
fn timeout(&self) -> Duration;
fn set_baud_rate(&mut self, baud_rate: u32) -> Result<()>;
fn set_data_bits(&mut self, data_bits: DataBits) -> Result<()>;
fn set_flow_control(&mut self, flow_control: FlowControl) -> Result<()>;
fn set_parity(&mut self, parity: Parity) -> Result<()>;
fn set_stop_bits(&mut self, stop_bits: StopBits) -> Result<()>;
fn set_timeout(&mut self, timeout: Duration) -> Result<()>;
fn write_request_to_send(&mut self, level: bool) -> Result<()>;
fn write_data_terminal_ready(&mut self, level: bool) -> Result<()>;
fn read_clear_to_send(&mut self) -> Result<bool>;
fn read_data_set_ready(&mut self) -> Result<bool>;
fn read_ring_indicator(&mut self) -> Result<bool>;
fn read_carrier_detect(&mut self) -> Result<bool>;
fn bytes_to_read(&self) -> Result<u32>;
fn bytes_to_write(&self) -> Result<u32>;
fn clear(&self, buffer_to_clear: ClearBuffer) -> Result<()>;
fn try_clone(&self) -> Result<Box<dyn SerialPort>>;
fn set_break(&self) -> Result<()>;
fn clear_break(&self) -> Result<()>;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UsbPortInfo {
pub vid: u16,
pub pid: u16,
pub serial_number: Option<String>,
pub manufacturer: Option<String>,
pub product: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SerialPortType {
UsbPort(UsbPortInfo),
PciPort,
BluetoothPort,
Unknown,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SerialPortInfo {
pub port_name: String,
pub port_type: SerialPortType,
}
pub fn new<'a>(path: impl Into<std::borrow::Cow<'a, str>>, baud_rate: u32) -> SerialPortBuilder {
SerialPortBuilder {
path: path.into().into_owned(),
baud_rate,
data_bits: DataBits::Eight,
flow_control: FlowControl::None,
parity: Parity::None,
stop_bits: StopBits::One,
timeout: Duration::from_millis(0),
}
}
pub fn available_ports() -> Result<Vec<SerialPortInfo>> {
#[cfg(unix)]
return crate::posix::available_ports();
#[cfg(windows)]
return crate::windows::available_ports();
#[cfg(not(any(unix, windows)))]
Err(Error::new(
ErrorKind::Unknown,
"available_ports() not implemented for platform",
))
}