use std::fmt::Debug;
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::sync::Arc;
use futures_lite::{Stream, StreamExt};
use static_assertions::assert_impl_all;
use crate::backend::{Backend, BackendType, DynBackend};
use crate::device::DeviceFeatureHandle;
use crate::{DeviceReader, DeviceReaderWriter, DeviceWriter, HidResult};
#[non_exhaustive]
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum DeviceId {
#[cfg(target_os = "windows")]
UncPath(windows::core::HSTRING),
#[cfg(target_os = "linux")]
DevPath(std::path::PathBuf),
#[cfg(target_os = "macos")]
RegistryEntryId(u64)
}
assert_impl_all!(DeviceId: Send, Sync, Unpin);
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct DeviceInfo {
pub id: DeviceId,
pub name: String,
pub manufacturer: Option<String>,
pub product_id: u16,
pub vendor_id: u16,
pub usage_id: u16,
pub usage_page: u16,
pub serial_number: Option<String>
}
assert_impl_all!(DeviceInfo: Send, Sync, Unpin);
impl DeviceInfo {
pub fn matches(&self, usage_page: u16, usage_id: u16, vendor_id: u16, product_id: u16) -> bool {
self.usage_page == usage_page && self.usage_id == usage_id && self.vendor_id == vendor_id && self.product_id == product_id
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub enum DeviceEvent {
Connected(DeviceId),
Disconnected(DeviceId)
}
#[derive(Default, Clone)]
pub struct HidBackend(Arc<DynBackend>);
impl HidBackend {
pub fn new(backend: BackendType) -> Self {
Self(Arc::new(DynBackend::new(backend)))
}
pub async fn enumerate(&self) -> HidResult<impl Stream<Item = Device> + Send + Unpin + use<'_>> {
let steam = self.0.enumerate().await?.filter_map(|result| match result {
Ok(info) => Some(Device {
backend: self.0.clone(),
device_info: info
}),
Err(_) => None
});
Ok(steam)
}
pub async fn query_devices(&self, id: &DeviceId) -> HidResult<impl Iterator<Item = Device> + use<'_>> {
Ok(self.0.query_info(id).await?.into_iter().map(|info| Device {
backend: self.0.clone(),
device_info: info
}))
}
pub fn watch(&self) -> HidResult<impl Stream<Item = DeviceEvent> + Send + Unpin> {
self.0.watch()
}
}
pub struct Device {
backend: Arc<DynBackend>,
device_info: DeviceInfo
}
impl Debug for Device {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Device")
.field("device_info", &self.device_info)
.finish_non_exhaustive()
}
}
impl PartialEq for Device {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.backend, &other.backend) && DeviceInfo::eq(&self.device_info, &other.device_info)
}
}
impl Eq for Device {}
impl Hash for Device {
fn hash<H: Hasher>(&self, state: &mut H) {
DeviceInfo::hash(&self.device_info, state)
}
}
impl Deref for Device {
type Target = DeviceInfo;
fn deref(&self) -> &Self::Target {
&self.device_info
}
}
impl Device {
pub fn to_device_info(self) -> DeviceInfo {
self.device_info
}
pub async fn open_readable(&self) -> HidResult<DeviceReader> {
let (r, _) = self.backend.open(&self.id, true, false).await?;
Ok(DeviceReader(r.unwrap()))
}
pub async fn open_writeable(&self) -> HidResult<DeviceWriter> {
let (_, w) = self.backend.open(&self.id, false, true).await?;
Ok(DeviceWriter(w.unwrap()))
}
pub async fn open(&self) -> HidResult<DeviceReaderWriter> {
let (r, w) = self.backend.open(&self.id, true, true).await?;
Ok((DeviceReader(r.unwrap()), DeviceWriter(w.unwrap())))
}
pub async fn open_feature_handle(&self) -> HidResult<DeviceFeatureHandle> {
let r = self.backend.open_feature_handle(&self.id).await?;
Ok(DeviceFeatureHandle(r))
}
pub async fn read_feature_report(&self, buf: &mut [u8]) -> HidResult<usize> {
self.backend.read_feature_report(&self.id, buf).await
}
}