streamdeck/src/port.rs

132 lines
3.5 KiB
Rust

use serialport::SerialPort;
use std::{
io::{BufRead, BufReader, Write},
time::Duration,
};
pub(crate) struct Port(Inner<Box<dyn SerialPort>>);
pub(crate) struct LinesReader(Inner<BufReader<Box<dyn SerialPort>>>);
pub(crate) struct BytesReader(Inner<Box<dyn SerialPort>>);
struct Inner<I>(Option<I>);
impl<I> Inner<I> {
async fn with_inner<F, T, E>(&mut self, f: F) -> Result<T, anyhow::Error>
where
I: Send + 'static,
F: Fn(I) -> (I, Result<T, E>) + Send + 'static,
T: Send + 'static,
E: Send + 'static,
anyhow::Error: From<E>,
{
let item = self
.0
.take()
.ok_or_else(|| anyhow::anyhow!("Item has been dropped"))?;
let (item, res) = tokio::task::spawn_blocking(move || (f)(item)).await?;
self.0 = Some(item);
res.map_err(From::from)
}
fn as_ref(&self) -> Option<&I> {
self.0.as_ref()
}
fn take(&mut self) -> Option<I> {
self.0.take()
}
}
impl Port {
pub(crate) async fn open(name: String) -> Result<Self, anyhow::Error> {
tokio::task::spawn_blocking(move || {
serialport::new(name, 9600)
.timeout(Duration::from_secs(1))
.open()
.map_err(From::from)
})
.await?
.map(Some)
.map(Inner)
.map(Port)
}
pub(crate) fn try_clone(&self) -> Result<Self, anyhow::Error> {
if let Some(port) = self.0.as_ref() {
Ok(Port(Inner(Some(port.try_clone()?))))
} else {
Err(anyhow::anyhow!("Port has been dropped"))
}
}
pub(crate) async fn write(&mut self, bytes: Vec<u8>) -> Result<(), anyhow::Error> {
self.0
.with_inner(move |mut port| {
let res = port.write_all(&bytes);
(port, res)
})
.await
}
pub(crate) fn lines(mut self) -> LinesReader {
LinesReader(Inner(self.0.take().map(BufReader::new)))
}
pub(crate) fn bytes(mut self) -> BytesReader {
BytesReader(Inner(self.0.take()))
}
pub(crate) async fn bytes_to_write(&mut self) -> Result<u32, anyhow::Error> {
self.0
.with_inner(move |port| {
let res = port.bytes_to_write();
(port, res)
})
.await
}
}
impl BytesReader {
pub(crate) async fn read_byte(&mut self) -> Result<u8, anyhow::Error> {
self.0
.with_inner(move |mut port| {
let mut bytes = [0u8; 1];
let res = port.read_exact(&mut bytes);
(port, res.map(|_| bytes[0]))
})
.await
}
}
impl LinesReader {
pub(crate) async fn read_line(&mut self) -> Result<String, anyhow::Error> {
self.0
.with_inner(move |mut reader| {
let mut s = String::new();
let res = reader.read_line(&mut s);
(reader, res.map(|_| s))
})
.await
}
}
impl std::fmt::Debug for Port {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(name) = self.0.as_ref().and_then(|p| p.name()) {
f.debug_struct("Port").field("name", &name).finish()
} else {
f.debug_struct("Port").finish()
}
}
}
impl std::fmt::Debug for LinesReader {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LinesReader").finish()
}
}