use crate::errors::{PrinterError, Result};
#[cfg(feature = "hidapi")]
use hidapi::{HidApi, HidDevice};
#[cfg(feature = "native_usb")]
use nusb::{MaybeFuture, transfer::EndpointType};
#[cfg(feature = "usb")]
use rusb::{Context, DeviceHandle, Direction, TransferType, UsbContext, UsbOption};
#[cfg(feature = "serial_port")]
use serialport::SerialPort;
use std::sync::{Arc, Mutex};
use std::{
fs::File,
io::{self, Read, Write},
net::{IpAddr, SocketAddr, TcpStream},
path::Path,
time::Duration,
};
const DEFAULT_TIMEOUT_SECONDS: u64 = 5;
pub trait Driver {
fn name(&self) -> String;
fn write(&self, data: &[u8]) -> Result<()>;
fn read(&self, buf: &mut [u8]) -> Result<usize>;
fn flush(&self) -> Result<()>;
}
#[derive(Default, Clone)]
pub struct ConsoleDriver {
show_output: bool,
}
impl ConsoleDriver {
pub fn open(show_output: bool) -> Self {
Self { show_output }
}
}
impl Driver for ConsoleDriver {
fn name(&self) -> String {
"console".to_owned()
}
fn write(&self, data: &[u8]) -> Result<()> {
if self.show_output {
io::stdout().write_all(data)?
}
Ok(())
}
fn read(&self, _buf: &mut [u8]) -> Result<usize> {
Ok(0)
}
fn flush(&self) -> Result<()> {
Ok(())
}
}
#[derive(Clone)]
pub struct NetworkDriver {
host: String,
port: u16,
stream: Arc<Mutex<TcpStream>>,
timeout: Duration,
}
impl NetworkDriver {
pub fn open(host: &str, port: u16, timeout: Option<Duration>) -> Result<Self> {
let stream = match timeout {
Some(timeout) => {
let addr = SocketAddr::new(
host.parse::<IpAddr>().map_err(|e| PrinterError::Io(e.to_string()))?,
port,
);
TcpStream::connect_timeout(&addr, timeout)?
}
None => TcpStream::connect((host, port))?,
};
let timeout = timeout.unwrap_or(Duration::from_secs(DEFAULT_TIMEOUT_SECONDS));
Ok(Self {
host: host.to_string(),
port,
stream: Arc::new(Mutex::new(stream)),
timeout,
})
}
}
impl Driver for NetworkDriver {
fn name(&self) -> String {
format!("network ({}:{})", self.host, self.port)
}
fn write(&self, data: &[u8]) -> Result<()> {
let mut stream = self.stream.lock()?;
stream.set_write_timeout(Some(self.timeout))?;
Ok(stream.write_all(data)?)
}
fn read(&self, buf: &mut [u8]) -> Result<usize> {
let mut stream = self.stream.lock()?;
stream.set_read_timeout(Some(self.timeout))?;
Ok(stream.read(buf)?)
}
fn flush(&self) -> Result<()> {
Ok(self.stream.lock()?.flush()?)
}
}
#[derive(Clone)]
pub struct FileDriver {
path: String,
file: Arc<Mutex<File>>,
}
impl FileDriver {
pub fn open(path: &Path) -> Result<Self> {
let file = File::options().read(true).append(true).open(path)?;
Ok(Self {
path: path.to_string_lossy().to_string(),
file: Arc::new(Mutex::new(file)),
})
}
}
impl Driver for FileDriver {
fn name(&self) -> String {
format!("file ({})", self.path)
}
fn write(&self, data: &[u8]) -> Result<()> {
self.file.lock()?.write_all(data)?;
Ok(())
}
fn read(&self, buf: &mut [u8]) -> Result<usize> {
Ok(self.file.lock()?.read(buf)?)
}
fn flush(&self) -> Result<()> {
Ok(self.file.lock()?.flush()?)
}
}
#[cfg(feature = "usb")]
#[derive(Clone)]
pub struct UsbDriver {
vendor_id: u16,
product_id: u16,
output_endpoint: u8,
input_endpoint: u8,
device: Arc<Mutex<DeviceHandle<Context>>>,
timeout: Duration,
}
#[cfg(feature = "usb")]
impl UsbDriver {
pub fn open(
vendor_id: u16,
product_id: u16,
timeout: Option<Duration>,
options: Option<&[UsbOption]>,
) -> Result<Self> {
let context = if let Some(options) = options {
Context::with_options(options).map_err(|e| PrinterError::Io(e.to_string()))?
} else {
Context::new().map_err(|e| PrinterError::Io(e.to_string()))?
};
let devices = context.devices().map_err(|e| PrinterError::Io(e.to_string()))?;
for device in devices.iter() {
let device_descriptor = device
.device_descriptor()
.map_err(|e| PrinterError::Io(e.to_string()))?;
if device_descriptor.vendor_id() == vendor_id && device_descriptor.product_id() == product_id {
let config_descriptor = device
.active_config_descriptor()
.map_err(|e| PrinterError::Io(e.to_string()))?;
let (output_endpoint, input_endpoint, interface_number) = config_descriptor
.interfaces()
.flat_map(|interface| interface.descriptors())
.flat_map(|descriptor| {
let interface_number = descriptor.interface_number();
let mut input_endpoint = None;
let mut output_endpoint = None;
for endpoint in descriptor.endpoint_descriptors() {
if endpoint.transfer_type() == TransferType::Bulk && endpoint.direction() == Direction::In {
input_endpoint = Some(endpoint.address());
} else if endpoint.transfer_type() == TransferType::Bulk
&& endpoint.direction() == Direction::Out
{
output_endpoint = Some(endpoint.address());
}
}
match (output_endpoint, input_endpoint) {
(Some(output_endpoint), Some(input_endpoint)) => {
Some((output_endpoint, input_endpoint, interface_number))
}
_ => None,
}
})
.next()
.ok_or_else(|| {
PrinterError::Io("no suitable endpoints or interface number found for USB device".to_string())
})?;
return match device.open() {
Ok(device_handle) => {
#[cfg(not(target_os = "windows"))]
match device_handle.kernel_driver_active(interface_number) {
Ok(active) => {
if active {
if let Err(e) = device_handle.detach_kernel_driver(interface_number) {
return Err(PrinterError::Io(e.to_string()));
}
}
}
Err(e) => return Err(PrinterError::Io(e.to_string())),
}
device_handle
.claim_interface(interface_number)
.map_err(|e| PrinterError::Io(e.to_string()))?;
Ok(Self {
vendor_id,
product_id,
output_endpoint,
input_endpoint,
device: Arc::new(Mutex::new(device_handle)),
timeout: timeout.unwrap_or(Duration::from_secs(DEFAULT_TIMEOUT_SECONDS)),
})
}
Err(e) => Err(PrinterError::Io(e.to_string())),
};
}
}
Err(PrinterError::Io("USB device not found".to_string()))
}
}
#[cfg(feature = "usb")]
impl Driver for UsbDriver {
fn name(&self) -> String {
format!(
"USB (VID: {}, PID: {}, output endpoint: {}, input endpoint: {})",
self.vendor_id, self.product_id, self.output_endpoint, self.input_endpoint
)
}
fn write(&self, data: &[u8]) -> Result<()> {
self.device
.lock()?
.write_bulk(self.output_endpoint, data, self.timeout)
.map_err(|e| PrinterError::Io(e.to_string()))?;
Ok(())
}
fn read(&self, buf: &mut [u8]) -> Result<usize> {
self.device
.lock()?
.read_bulk(self.input_endpoint, buf, self.timeout)
.map_err(|e| PrinterError::Io(e.to_string()))
}
fn flush(&self) -> Result<()> {
Ok(())
}
}
#[cfg(feature = "native_usb")]
#[derive(Clone)]
pub struct NativeUsbDriver {
vendor_id: u16,
product_id: u16,
output_endpoint: u8,
input_endpoint: u8,
device: Arc<Mutex<nusb::Interface>>,
}
#[cfg(feature = "native_usb")]
impl NativeUsbDriver {
pub fn open(vendor_id: u16, product_id: u16) -> Result<Self> {
let device_info = nusb::list_devices()
.wait()
.map_err(|e| PrinterError::Io(e.to_string()))?
.find(|dev| dev.vendor_id() == vendor_id && dev.product_id() == product_id)
.ok_or(PrinterError::Io("USB device not found".to_string()))?;
let device = device_info.open().wait().map_err(|e| PrinterError::Io(e.to_string()))?;
let configuration = device
.active_configuration()
.map_err(|e| PrinterError::Io(e.to_string()))?;
let (output_endpoint, input_endpoint) = match configuration.interface_alt_settings().next() {
Some(settings) => {
let endpoints = settings.endpoints();
let (mut output, mut input) = (None, None);
for endpoint in endpoints {
if endpoint.transfer_type() == nusb::transfer::Bulk::TYPE
&& endpoint.direction() == nusb::transfer::Direction::Out
{
output = Some(endpoint.address())
} else if endpoint.transfer_type() == nusb::transfer::Bulk::TYPE
&& endpoint.direction() == nusb::transfer::Direction::In
{
input = Some(endpoint.address())
}
}
match (output, input) {
(Some(output), Some(input)) => Some((output, input)),
_ => None,
}
}
None => None,
}
.ok_or(PrinterError::Io(
"no suitable input or output endpoints found for USB device".to_string(),
))?;
let interface_number = device_info
.interfaces()
.map(|interface| interface.interface_number())
.next()
.ok_or_else(|| PrinterError::Io("no suitable interface number found for USB device".to_string()))?;
let interface = device
.detach_and_claim_interface(interface_number)
.wait()
.map_err(|e| PrinterError::Io(e.to_string()))?;
Ok(Self {
vendor_id,
product_id,
output_endpoint,
input_endpoint,
device: Arc::new(Mutex::new(interface)),
})
}
}
#[cfg(feature = "native_usb")]
impl Driver for NativeUsbDriver {
fn name(&self) -> String {
format!(
"USB (VID: {}, PID: {}, output endpoint: {}, input endpoint: {})",
self.vendor_id, self.product_id, self.output_endpoint, self.input_endpoint
)
}
fn write(&self, data: &[u8]) -> Result<()> {
let endpoint = self
.device
.lock()?
.endpoint::<nusb::transfer::Bulk, nusb::transfer::Out>(self.output_endpoint)
.map_err(|e| PrinterError::Io(e.to_string()))?;
let max_size = endpoint.max_packet_size();
let mut writer = endpoint
.writer(max_size)
.with_write_timeout(Duration::from_secs(DEFAULT_TIMEOUT_SECONDS));
writer.write_all(data).map_err(|e| PrinterError::Io(e.to_string()))?;
writer.flush().map_err(|e| PrinterError::Io(e.to_string()))?;
Ok(())
}
fn read(&self, buf: &mut [u8]) -> Result<usize> {
let endpoint = self
.device
.lock()?
.endpoint::<nusb::transfer::Bulk, nusb::transfer::In>(self.input_endpoint)
.map_err(|e| PrinterError::Io(e.to_string()))?;
let max_size = endpoint.max_packet_size();
let mut reader = endpoint
.reader(max_size)
.with_read_timeout(Duration::from_secs(DEFAULT_TIMEOUT_SECONDS));
let mut pkt_reader = reader.until_short_packet();
let size = pkt_reader
.read_to_end(&mut buf.to_vec())
.map_err(|e| PrinterError::Io(e.to_string()))?;
pkt_reader.consume_end().map_err(|e| PrinterError::Io(e.to_string()))?;
Ok(size)
}
fn flush(&self) -> Result<()> {
Ok(())
}
}
#[cfg(feature = "hidapi")]
#[derive(Clone)]
pub struct HidApiDriver {
vendor_id: u16,
product_id: u16,
device: Arc<Mutex<HidDevice>>,
}
#[cfg(feature = "hidapi")]
impl HidApiDriver {
pub fn open(vendor_id: u16, product_id: u16) -> Result<Self> {
let api = HidApi::new().map_err(|e| PrinterError::Io(e.to_string()))?;
let device = api
.open(vendor_id, product_id)
.map_err(|e| PrinterError::Io(e.to_string()))?;
Ok(Self {
vendor_id,
product_id,
device: Arc::new(Mutex::new(device)),
})
}
}
#[cfg(feature = "hidapi")]
impl Driver for HidApiDriver {
fn name(&self) -> String {
format!("HidApi (VID: {}, PID: {})", self.vendor_id, self.product_id)
}
fn write(&self, data: &[u8]) -> Result<()> {
self.device
.lock()?
.write(data)
.map_err(|e| PrinterError::Io(e.to_string()))?;
Ok(())
}
fn read(&self, buf: &mut [u8]) -> Result<usize> {
self.device
.lock()?
.read_timeout(buf, i32::try_from(DEFAULT_TIMEOUT_SECONDS * 1_000)?)
.map_err(|e| PrinterError::Io(e.to_string()))
}
fn flush(&self) -> Result<()> {
Ok(())
}
}
#[cfg(feature = "serial_port")]
#[derive(Clone)]
pub struct SerialPortDriver {
path: String,
port: Arc<Mutex<Box<dyn SerialPort>>>,
}
#[cfg(feature = "serial_port")]
impl SerialPortDriver {
pub fn open(path: &str, baud_rate: u32, timeout: Option<Duration>) -> Result<Self> {
let mut port = serialport::new(path, baud_rate);
if let Some(timeout) = timeout {
port = port.timeout(timeout);
}
let port = port.open().map_err(|e| PrinterError::Io(e.to_string()))?;
Ok(Self {
path: path.to_string(),
port: Arc::new(Mutex::new(port)),
})
}
}
#[cfg(feature = "serial_port")]
impl Driver for SerialPortDriver {
fn name(&self) -> String {
format!("Serial port ({})", self.path)
}
fn write(&self, data: &[u8]) -> Result<()> {
self.port.lock()?.write_all(data)?;
Ok(())
}
fn read(&self, buf: &mut [u8]) -> Result<usize> {
let mut port = self.port.lock()?;
port.set_timeout(Duration::from_secs(DEFAULT_TIMEOUT_SECONDS))
.map_err(|e| PrinterError::Io(e.to_string()))?;
Ok(port.read(buf)?)
}
fn flush(&self) -> Result<()> {
Ok(self.port.lock()?.flush()?)
}
}