use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; use tokio_serial::SerialStream; use tracing_error::SpanTrace; #[derive(Debug)] pub struct Error { kind: ErrorKind, context: SpanTrace, } #[derive(Debug, thiserror::Error)] pub enum ErrorKind { #[error(transparent)] Serial(#[from] tokio_serial::Error), #[error(transparent)] Io(#[from] std::io::Error), #[error(transparent)] Utf8(#[from] std::string::FromUtf8Error), } pub async fn identity(port: &mut SerialStream) -> Result, Error> { get_prop(port, b"ident").await } pub async fn name(port: &mut SerialStream) -> Result { get_string_prop(port, b"name").await } pub async fn author(port: &mut SerialStream) -> Result { get_string_prop(port, b"author").await } pub async fn repo(port: &mut SerialStream) -> Result { get_string_prop(port, b"repo").await } pub async fn version(port: &mut SerialStream) -> Result { get_string_prop(port, b"version").await } pub async fn reset(port: &mut SerialStream) -> Result<(), Error> { send_command(port, b"reset").await } async fn send_command(port: &mut Port, command: &[u8]) -> Result<(), Error> where Port: AsyncWrite + Unpin, { port.write_all(command).await?; port.flush().await?; Ok(()) } async fn get_string_prop(port: &mut Port, prop: &[u8]) -> Result where Port: AsyncRead + AsyncWrite + Unpin, { Ok(String::from_utf8(get_prop(port, prop).await?)?) } async fn get_prop(port: &mut Port, prop: &[u8]) -> Result, Error> where Port: AsyncRead + AsyncWrite + Unpin, { send_command::(port, prop).await?; let mut len_buf = [0u8; 1]; port.read_exact(&mut len_buf).await?; let input_len = len_buf[0]; let mut input = vec![0u8; input_len as usize]; port.read_exact(&mut input).await?; Ok(input) } impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { writeln!(f, "{}", self.kind)?; std::fmt::Display::fmt(&self.context, f) } } impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { self.kind.source() } } impl From for Error where ErrorKind: From, { fn from(error: T) -> Self { Error { kind: error.into(), context: SpanTrace::capture(), } } }