1use std::fmt::Debug;
2use std::hash::{Hash, Hasher};
3use std::ops::Deref;
4use std::sync::Arc;
5
6use futures_lite::{Stream, StreamExt};
7use static_assertions::assert_impl_all;
8
9use crate::backend::{Backend, BackendType, DynBackend};
10use crate::{DeviceReader, DeviceReaderWriter, DeviceWriter, HidResult};
11
12#[non_exhaustive]
30#[derive(Debug, PartialEq, Eq, Clone, Hash)]
31pub enum DeviceId {
32 #[cfg(target_os = "windows")]
33 UncPath(windows::core::HSTRING),
34 #[cfg(target_os = "linux")]
35 DevPath(std::path::PathBuf),
36 #[cfg(target_os = "macos")]
37 RegistryEntryId(u64)
38}
39assert_impl_all!(DeviceId: Send, Sync, Unpin);
40
41#[derive(Debug, Clone, Hash, Eq, PartialEq)]
45pub struct DeviceInfo {
46 pub id: DeviceId,
48 pub name: String,
50 pub product_id: u16,
52 pub vendor_id: u16,
54 pub usage_id: u16,
56 pub usage_page: u16,
58 pub serial_number: Option<String>
60}
61assert_impl_all!(DeviceInfo: Send, Sync, Unpin);
62
63impl DeviceInfo {
64 pub fn matches(&self, usage_page: u16, usage_id: u16, vendor_id: u16, product_id: u16) -> bool {
66 self.usage_page == usage_page && self.usage_id == usage_id && self.vendor_id == vendor_id && self.product_id == product_id
67 }
68}
69
70#[derive(Debug, Clone, Hash, Eq, PartialEq)]
71pub enum DeviceEvent {
72 Connected(DeviceId),
73 Disconnected(DeviceId)
74}
75
76#[derive(Default, Clone)]
78pub struct HidBackend(Arc<DynBackend>);
79
80impl HidBackend {
81 pub fn new(backend: BackendType) -> Self {
84 Self(Arc::new(DynBackend::new(backend)))
85 }
86
87 pub async fn enumerate(&self) -> HidResult<impl Stream<Item = Device> + Send + Unpin + use<'_>> {
91 let steam = self.0.enumerate().await?.filter_map(|result| match result {
92 Ok(info) => Some(Device {
93 backend: self.0.clone(),
94 device_info: info
95 }),
96 Err(_) => None
97 });
98 Ok(steam)
99 }
100
101 pub async fn query_devices(&self, id: &DeviceId) -> HidResult<impl Iterator<Item = Device> + use<'_>> {
103 Ok(self.0.query_info(id).await?.into_iter().map(|info| Device {
104 backend: self.0.clone(),
105 device_info: info
106 }))
107 }
108
109 pub fn watch(&self) -> HidResult<impl Stream<Item = DeviceEvent> + Send + Unpin> {
113 self.0.watch()
114 }
115}
116
117pub struct Device {
119 backend: Arc<DynBackend>,
120 device_info: DeviceInfo
121}
122
123impl Debug for Device {
124 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125 f.debug_struct("Device")
126 .field("device_info", &self.device_info)
127 .finish_non_exhaustive()
128 }
129}
130
131impl PartialEq for Device {
132 fn eq(&self, other: &Self) -> bool {
133 Arc::ptr_eq(&self.backend, &other.backend) && DeviceInfo::eq(&self.device_info, &other.device_info)
134 }
135}
136impl Eq for Device {}
137
138impl Hash for Device {
139 fn hash<H: Hasher>(&self, state: &mut H) {
140 DeviceInfo::hash(&self.device_info, state)
141 }
142}
143
144impl Deref for Device {
145 type Target = DeviceInfo;
146
147 fn deref(&self) -> &Self::Target {
148 &self.device_info
149 }
150}
151
152impl Device {
153 pub fn to_device_info(self) -> DeviceInfo {
154 self.device_info
155 }
156
157 pub async fn open_readable(&self) -> HidResult<DeviceReader> {
159 let (r, _) = self.backend.open(&self.id, true, false).await?;
160 Ok(DeviceReader(r.unwrap()))
161 }
162
163 pub async fn open_writeable(&self) -> HidResult<DeviceWriter> {
166 let (_, w) = self.backend.open(&self.id, false, true).await?;
167 Ok(DeviceWriter(w.unwrap()))
168 }
169
170 pub async fn open(&self) -> HidResult<DeviceReaderWriter> {
172 let (r, w) = self.backend.open(&self.id, true, true).await?;
173 Ok((DeviceReader(r.unwrap()), DeviceWriter(w.unwrap())))
174 }
175}