#![allow(clippy::uninlined_format_args)]
#![deny(
clippy::dbg_macro,
missing_docs,
missing_debug_implementations,
missing_copy_implementations
)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc(test(attr(allow(unused_must_use))))]
use std::error::Error as StdError;
use std::fmt;
use std::io;
use std::str::FromStr;
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;
#[cfg(test)]
pub(crate) mod tests;
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, Clone)]
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)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum DataBits {
Five,
Six,
Seven,
Eight,
}
impl fmt::Display for DataBits {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
DataBits::Five => write!(f, "Five"),
DataBits::Six => write!(f, "Six"),
DataBits::Seven => write!(f, "Seven"),
DataBits::Eight => write!(f, "Eight"),
}
}
}
impl From<DataBits> for u8 {
fn from(value: DataBits) -> Self {
match value {
DataBits::Five => 5,
DataBits::Six => 6,
DataBits::Seven => 7,
DataBits::Eight => 8,
}
}
}
impl TryFrom<u8> for DataBits {
type Error = ();
fn try_from(value: u8) -> core::result::Result<Self, Self::Error> {
match value {
5 => Ok(Self::Five),
6 => Ok(Self::Six),
7 => Ok(Self::Seven),
8 => Ok(Self::Eight),
_ => Err(()),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Parity {
None,
Odd,
Even,
}
impl fmt::Display for Parity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Parity::None => write!(f, "None"),
Parity::Odd => write!(f, "Odd"),
Parity::Even => write!(f, "Even"),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum StopBits {
One,
Two,
}
impl fmt::Display for StopBits {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
StopBits::One => write!(f, "One"),
StopBits::Two => write!(f, "Two"),
}
}
}
impl From<StopBits> for u8 {
fn from(value: StopBits) -> Self {
match value {
StopBits::One => 1,
StopBits::Two => 2,
}
}
}
impl TryFrom<u8> for StopBits {
type Error = ();
fn try_from(value: u8) -> core::result::Result<Self, Self::Error> {
match value {
1 => Ok(Self::One),
2 => Ok(Self::Two),
_ => Err(()),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum FlowControl {
None,
Software,
Hardware,
}
impl fmt::Display for FlowControl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
FlowControl::None => write!(f, "None"),
FlowControl::Software => write!(f, "Software"),
FlowControl::Hardware => write!(f, "Hardware"),
}
}
}
impl FromStr for FlowControl {
type Err = ();
fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
match s {
"None" | "none" | "n" => Ok(FlowControl::None),
"Software" | "software" | "SW" | "sw" | "s" => Ok(FlowControl::Software),
"Hardware" | "hardware" | "HW" | "hw" | "h" => Ok(FlowControl::Hardware),
_ => Err(()),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
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,
dtr_on_open: Option<bool>,
#[cfg(unix)]
exclusive: bool,
}
impl SerialPortBuilder {
#[allow(clippy::assigning_clones)]
#[must_use]
pub fn path<'a>(mut self, path: impl Into<std::borrow::Cow<'a, str>>) -> Self {
self.path = path.into().as_ref().to_owned();
self
}
#[must_use]
pub fn baud_rate(mut self, baud_rate: u32) -> Self {
self.baud_rate = baud_rate;
self
}
#[must_use]
pub fn data_bits(mut self, data_bits: DataBits) -> Self {
self.data_bits = data_bits;
self
}
#[must_use]
pub fn flow_control(mut self, flow_control: FlowControl) -> Self {
self.flow_control = flow_control;
self
}
#[must_use]
pub fn parity(mut self, parity: Parity) -> Self {
self.parity = parity;
self
}
#[must_use]
pub fn stop_bits(mut self, stop_bits: StopBits) -> Self {
self.stop_bits = stop_bits;
self
}
#[must_use]
pub fn timeout(mut self, timeout: Duration) -> Self {
self.timeout = timeout;
self
}
#[must_use]
pub fn dtr_on_open(mut self, state: bool) -> Self {
self.dtr_on_open = Some(state);
self
}
#[must_use]
pub fn preserve_dtr_on_open(mut self) -> Self {
self.dtr_on_open = None;
self
}
#[cfg(unix)]
#[must_use]
pub fn exclusive(mut self, exclusive: bool) -> Self {
self.exclusive = exclusive;
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<()>;
}
impl<T: SerialPort> SerialPort for &mut T {
fn name(&self) -> Option<String> {
(**self).name()
}
fn baud_rate(&self) -> Result<u32> {
(**self).baud_rate()
}
fn data_bits(&self) -> Result<DataBits> {
(**self).data_bits()
}
fn flow_control(&self) -> Result<FlowControl> {
(**self).flow_control()
}
fn parity(&self) -> Result<Parity> {
(**self).parity()
}
fn stop_bits(&self) -> Result<StopBits> {
(**self).stop_bits()
}
fn timeout(&self) -> Duration {
(**self).timeout()
}
fn set_baud_rate(&mut self, baud_rate: u32) -> Result<()> {
(**self).set_baud_rate(baud_rate)
}
fn set_data_bits(&mut self, data_bits: DataBits) -> Result<()> {
(**self).set_data_bits(data_bits)
}
fn set_flow_control(&mut self, flow_control: FlowControl) -> Result<()> {
(**self).set_flow_control(flow_control)
}
fn set_parity(&mut self, parity: Parity) -> Result<()> {
(**self).set_parity(parity)
}
fn set_stop_bits(&mut self, stop_bits: StopBits) -> Result<()> {
(**self).set_stop_bits(stop_bits)
}
fn set_timeout(&mut self, timeout: Duration) -> Result<()> {
(**self).set_timeout(timeout)
}
fn write_request_to_send(&mut self, level: bool) -> Result<()> {
(**self).write_request_to_send(level)
}
fn write_data_terminal_ready(&mut self, level: bool) -> Result<()> {
(**self).write_data_terminal_ready(level)
}
fn read_clear_to_send(&mut self) -> Result<bool> {
(**self).read_clear_to_send()
}
fn read_data_set_ready(&mut self) -> Result<bool> {
(**self).read_data_set_ready()
}
fn read_ring_indicator(&mut self) -> Result<bool> {
(**self).read_ring_indicator()
}
fn read_carrier_detect(&mut self) -> Result<bool> {
(**self).read_carrier_detect()
}
fn bytes_to_read(&self) -> Result<u32> {
(**self).bytes_to_read()
}
fn bytes_to_write(&self) -> Result<u32> {
(**self).bytes_to_write()
}
fn clear(&self, buffer_to_clear: ClearBuffer) -> Result<()> {
(**self).clear(buffer_to_clear)
}
fn try_clone(&self) -> Result<Box<dyn SerialPort>> {
(**self).try_clone()
}
fn set_break(&self) -> Result<()> {
(**self).set_break()
}
fn clear_break(&self) -> Result<()> {
(**self).clear_break()
}
}
impl fmt::Debug for dyn SerialPort {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SerialPort ( ")?;
if let Some(n) = self.name().as_ref() {
write!(f, "name: {} ", n)?;
};
if let Ok(b) = self.baud_rate().as_ref() {
write!(f, "baud_rate: {} ", b)?;
};
if let Ok(b) = self.data_bits().as_ref() {
write!(f, "data_bits: {} ", b)?;
};
if let Ok(c) = self.flow_control().as_ref() {
write!(f, "flow_control: {} ", c)?;
}
if let Ok(p) = self.parity().as_ref() {
write!(f, "parity: {} ", p)?;
}
if let Ok(s) = self.stop_bits().as_ref() {
write!(f, "stop_bits: {} ", s)?;
}
write!(f, ")")
}
}
#[derive(Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct UsbPortInfo {
pub vid: u16,
pub pid: u16,
pub serial_number: Option<String>,
pub manufacturer: Option<String>,
pub product: Option<String>,
#[cfg(feature = "usbportinfo-interface")]
pub interface: Option<u8>,
}
struct HexU16(u16);
impl std::fmt::Debug for HexU16 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "0x{:04x}", self.0)
}
}
impl std::fmt::Debug for UsbPortInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut d = f.debug_struct("UsbPortInfo");
d.field("vid", &HexU16(self.vid))
.field("pid", &HexU16(self.pid))
.field("serial_number", &self.serial_number)
.field("manufacturer", &self.manufacturer)
.field("product", &self.product);
#[cfg(feature = "usbportinfo-interface")]
{
d.field("interface", &self.interface);
}
d.finish()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum SerialPortType {
UsbPort(UsbPortInfo),
PciPort,
BluetoothPort,
Unknown,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
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),
dtr_on_open: None,
#[cfg(unix)]
exclusive: true,
}
}
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",
))
}
#[cfg(test)]
mod test {
use super::*;
use rstest::rstest;
#[rstest]
fn builder_new() {
let builder = new("port_test_dummy", 12345);
assert_eq!(builder.path, "port_test_dummy");
assert_eq!(builder.baud_rate, 12345);
assert_eq!(builder.data_bits, DataBits::Eight);
assert_eq!(builder.flow_control, FlowControl::None);
assert_eq!(builder.parity, Parity::None);
assert_eq!(builder.stop_bits, StopBits::One);
assert_eq!(builder.timeout, Duration::ZERO);
assert_eq!(builder.dtr_on_open, None);
#[cfg(unix)]
assert!(builder.exclusive);
}
#[cfg(unix)]
#[rstest]
fn builder_exclusive() {
let builder = new("port_test_dummy", 12345);
assert!(builder.exclusive);
let builder = builder.exclusive(false);
assert!(!builder.exclusive);
let builder = builder.exclusive(true);
assert!(builder.exclusive);
}
#[rstest]
fn usbportinfo_debug_representation() {
let info = UsbPortInfo {
manufacturer: Some(String::from("your manufacutrer here")),
vid: 0xbade,
pid: 0xaffe,
product: Some(String::from("your product here")),
serial_number: Some(String::from("your serial_number here")),
#[cfg(feature = "usbportinfo-interface")]
interface: Some(42),
};
let formatted = format!("{:?}", info);
#[cfg(not(feature = "usbportinfo-interface"))]
let expected = "UsbPortInfo { vid: 0xbade, pid: 0xaffe, serial_number: Some(\"your serial_number here\"), manufacturer: Some(\"your manufacutrer here\"), product: Some(\"your product here\") }";
#[cfg(feature = "usbportinfo-interface")]
let expected = "UsbPortInfo { vid: 0xbade, pid: 0xaffe, serial_number: Some(\"your serial_number here\"), manufacturer: Some(\"your manufacutrer here\"), product: Some(\"your product here\"), interface: Some(42) }";
assert_eq!(formatted, expected);
}
}