use log::debug;
use nom_derive::Parse;
use std::{
fs::File,
io::{Read, Write},
path::{Path, PathBuf},
sync::{
mpsc::{self, SendError},
Mutex,
},
thread::JoinHandle,
};
use thiserror::Error;
pub mod util;
use util::ReadExt as _;
use crate::controls::{ControlCommand, ControlPacket};
#[derive(Debug, Error)]
pub enum ReadControlError {
#[error(transparent)]
IoError(#[from] std::io::Error),
#[error("Error parsing control packet: {0}")]
ParseError(String),
}
#[derive(Debug, Error)]
pub enum ControlChannelError {
#[error(transparent)]
ReadControl(#[from] ReadControlError),
#[error("Cannot send control packet to channel")]
CannotSend,
}
impl<T> From<SendError<T>> for ControlChannelError {
fn from(_: SendError<T>) -> Self {
ControlChannelError::CannotSend
}
}
pub struct ChannelExtcapControlReader {
pub join_handle: JoinHandle<Result<(), ControlChannelError>>,
pub read_channel: mpsc::Receiver<ControlPacket<'static>>,
}
impl ChannelExtcapControlReader {
pub fn spawn(in_path: PathBuf) -> Self {
let (tx, rx) = mpsc::sync_channel::<ControlPacket<'static>>(10);
let join_handle = std::thread::spawn(move || {
let reader = ExtcapControlReader::new(&in_path);
loop {
tx.send(reader.read_control_packet()?)?;
}
});
Self {
join_handle,
read_channel: rx,
}
}
pub fn try_read_packet(&self) -> Option<ControlPacket<'static>> {
self.read_channel.try_recv().ok()
}
pub fn read_packet(&self) -> Result<ControlPacket<'static>, mpsc::RecvError> {
self.read_channel.recv()
}
}
pub struct ExtcapControlReader {
in_file: File,
}
impl ExtcapControlReader {
pub fn new(in_path: &Path) -> Self {
Self {
in_file: File::open(in_path).unwrap(),
}
}
pub fn read_control_packet(&self) -> Result<ControlPacket<'static>, ReadControlError> {
let mut in_file = &self.in_file;
let header_bytes = in_file
.try_read_exact::<6>()?
.ok_or_else(|| std::io::Error::from(std::io::ErrorKind::UnexpectedEof))?;
debug!(
"Read header bytes from incoming control message, now parsing... {:?}",
header_bytes
);
let (_rem, packet) = match ControlPacket::parse(&header_bytes) {
Ok((rem, packet)) => (rem, packet.into_owned()),
Err(nom::Err::Incomplete(nom::Needed::Size(size))) => {
let mut payload_bytes = vec![0_u8; size.get()];
in_file.read_exact(&mut payload_bytes)?;
let all_bytes = [header_bytes.as_slice(), payload_bytes.as_slice()].concat();
ControlPacket::parse(&all_bytes)
.map(|(_, packet)| (&[][..], packet.into_owned()))
.unwrap_or_else(|e| panic!("Unable to parse header packet: {e}"))
}
Err(e) => Err(ReadControlError::ParseError(e.to_string()))?,
};
debug!("Parsed incoming control message: {packet:?}");
Ok(packet)
}
}
const UNUSED_CONTROL_NUMBER: u8 = 255;
pub trait ExtcapControlSenderTrait: Sized {
fn send(self, packet: ControlPacket<'_>) -> std::io::Result<()>;
fn info_message(self, message: &str) -> std::io::Result<()> {
self.send(ControlPacket::new_with_payload(
UNUSED_CONTROL_NUMBER,
ControlCommand::InformationMessage,
message.as_bytes(),
))
}
fn warning_message(self, message: &str) -> std::io::Result<()> {
self.send(ControlPacket::new_with_payload(
UNUSED_CONTROL_NUMBER,
ControlCommand::WarningMessage,
message.as_bytes(),
))
}
fn error_message(self, message: &str) -> std::io::Result<()> {
self.send(ControlPacket::new_with_payload(
UNUSED_CONTROL_NUMBER,
ControlCommand::ErrorMessage,
message.as_bytes(),
))
}
fn status_message(self, message: &str) -> std::io::Result<()> {
self.send(ControlPacket::new_with_payload(
UNUSED_CONTROL_NUMBER,
ControlCommand::StatusbarMessage,
message.as_bytes(),
))
}
}
pub struct ExtcapControlSender {
out_file: File,
}
impl ExtcapControlSender {
pub fn new(out_path: &Path) -> Self {
Self {
out_file: File::create(out_path).unwrap(),
}
}
}
impl ExtcapControlSenderTrait for &mut ExtcapControlSender {
fn send(self, packet: ControlPacket<'_>) -> std::io::Result<()> {
self.out_file.write_all(&packet.to_header_bytes())?;
self.out_file.write_all(&packet.payload)?;
self.out_file.flush().unwrap();
Ok(())
}
}
impl<T> ExtcapControlSenderTrait for &mut Option<T>
where
for<'a> &'a mut T: ExtcapControlSenderTrait,
{
fn send(self, packet: ControlPacket<'_>) -> Result<(), tokio::io::Error> {
if let Some(s) = self {
s.send(packet)
} else {
Ok(())
}
}
}
impl<T> ExtcapControlSenderTrait for &Mutex<T>
where
for<'a> &'a mut T: ExtcapControlSenderTrait,
{
fn send(self, packet: ControlPacket<'_>) -> Result<(), tokio::io::Error> {
self.lock().unwrap().send(packet)
}
}