use io::Write;
use std::fmt;
use std::io;
use thiserror::Error;
mod enttec;
mod offline;
pub use enttec::EnttecDmxPort;
pub use offline::OfflineDmxPort;
#[typetag::serde(tag = "type")]
pub trait DmxPort: fmt::Display {
fn available_ports() -> anyhow::Result<PortListing>
where
Self: Sized;
fn open(&mut self) -> Result<(), OpenError>;
fn close(&mut self);
fn write(&mut self, frame: &[u8]) -> Result<(), WriteError>;
}
type PortListing = Vec<Box<dyn DmxPort>>;
pub fn available_ports() -> anyhow::Result<PortListing> {
let mut ports = Vec::new();
ports.extend(OfflineDmxPort::available_ports()?);
ports.extend(EnttecDmxPort::available_ports()?);
Ok(ports)
}
pub fn select_port() -> anyhow::Result<Box<dyn DmxPort>> {
let mut ports = available_ports()?;
println!("Available DMX ports:");
for (i, port) in ports.iter().enumerate() {
println!("{}: {}", i, port);
}
let mut port = loop {
print!("Select a port: ");
io::stdout().flush()?;
let input = read_string()?;
let index = match input.trim().parse::<usize>() {
Ok(num) => num,
Err(e) => {
println!("{}; please enter an integer.", e);
continue;
}
};
if index >= ports.len() {
println!("Please enter a value less than {}.", ports.len());
continue;
}
break ports.swap_remove(index);
};
port.open()?;
Ok(port)
}
fn read_string() -> Result<String, io::Error> {
let mut line = String::new();
io::stdin().read_line(&mut line)?;
Ok(line.trim().to_string())
}
#[derive(Error, Debug)]
pub enum OpenError {
#[error("the DMX port is not connected")]
NotConnected,
#[error(transparent)]
Other(#[from] anyhow::Error),
}
#[derive(Error, Debug)]
pub enum WriteError {
#[error("the DMX port is not connected")]
Disconnected,
#[error(transparent)]
Other(#[from] anyhow::Error),
}