1use std::fmt::Debug;
2use std::future::Future;
3use std::hash::Hash;
4
5use futures_lite::stream::Boxed;
6
7use crate::device_info::DeviceId;
8use crate::traits::{AsyncHidRead, AsyncHidWrite};
9use crate::{DeviceEvent, DeviceInfo, HidResult};
10
11pub type DeviceInfoStream = Boxed<HidResult<DeviceInfo>>;
12pub trait Backend: Sized + Default {
13 type Reader: AsyncHidRead + Send + Sync;
14 type Writer: AsyncHidWrite + Send + Sync;
15
16 fn enumerate(&self) -> impl Future<Output = HidResult<DeviceInfoStream>> + Send;
17 fn watch(&self) -> HidResult<Boxed<DeviceEvent>>;
18
19 fn query_info(&self, id: &DeviceId) -> impl Future<Output = HidResult<Vec<DeviceInfo>>> + Send;
20
21 #[allow(clippy::type_complexity)]
22 fn open(&self, id: &DeviceId, read: bool, write: bool) -> impl Future<Output = HidResult<(Option<Self::Reader>, Option<Self::Writer>)>> + Send;
23}
24
25macro_rules! dyn_backend_impl {
26 {
27 $(
28 $(#[$module_attrs:meta])*
29 mod $module:ident {
30 $(#[$item_attrs:meta])*
31 $name:ident($backend:ty)
32 }
33 )+
34 } => {
35 $(
36 $(#[$module_attrs])*
37 mod $module;
38 )+
39
40 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
41 #[non_exhaustive]
42 pub enum BackendType {
43 $(
44 $(#[$module_attrs])*$(#[$item_attrs])*
45 $name,
46 )+
47 }
48
49 pub enum DynReader {
50 $(
51 $(#[$module_attrs])*$(#[$item_attrs])*
52 $name(<$backend as Backend>::Reader),
53 )+
54 }
55 impl AsyncHidRead for DynReader {
56 async fn read_input_report<'a>(&'a mut self, buf: &'a mut [u8]) -> HidResult<usize> {
57 match self {
58 $(
59 $(#[$module_attrs])*$(#[$item_attrs])*
60 Self::$name(i) => i.read_input_report(buf).await,
61 )+
62 }
63 }
64 }
65
66 pub enum DynWriter {
67 $(
68 $(#[$module_attrs])*$(#[$item_attrs])*
69 $name(<$backend as Backend>::Writer),
70 )+
71 }
72 impl AsyncHidWrite for DynWriter {
73 async fn write_output_report<'a>(&'a mut self, buf: &'a [u8]) -> HidResult<()> {
74 match self {
75 $(
76 $(#[$module_attrs])*$(#[$item_attrs])*
77 Self::$name(i) => i.write_output_report(buf).await,
78 )+
79 }
80 }
81 }
82
83 pub enum DynBackend {
84 $(
85 $(#[$module_attrs])*$(#[$item_attrs])*
86 $name($backend),
87 )+
88 }
89 impl DynBackend {
90 pub fn new(backend: BackendType) -> DynBackend {
91 match backend {
92 $(
93 $(#[$module_attrs])*$(#[$item_attrs])*
94 BackendType::$name => Self::$name(<$backend as Default>::default()),
95 )+
96 }
97 }
98 }
99 impl Backend for DynBackend {
100 type Reader = DynReader;
101 type Writer = DynWriter;
102
103
104 async fn enumerate(&self) -> HidResult<DeviceInfoStream> {
105 match self {
106 $(
107 $(#[$module_attrs])*$(#[$item_attrs])*
108 Self::$name(i) => i.enumerate().await,
109 )+
110 }
111 }
112
113 fn watch(&self) -> HidResult<Boxed<DeviceEvent>> {
114 match self {
115 $(
116 $(#[$module_attrs])*$(#[$item_attrs])*
117 Self::$name(i) => i.watch(),
118 )+
119 }
120 }
121
122 async fn query_info(&self, id: &DeviceId) -> HidResult<Vec<DeviceInfo>> {
123 match self {
124 $(
125 $(#[$module_attrs])*$(#[$item_attrs])*
126 Self::$name(i) => i.query_info(id).await,
127 )+
128 }
129 }
130
131 async fn open(&self, id: &DeviceId, read: bool, write: bool) -> HidResult<(Option<Self::Reader>, Option<Self::Writer>)> {
132 match self {
133 $(
134 $(#[$module_attrs])*$(#[$item_attrs])*
135 Self::$name(i) => i.open(id, read, write).await.map(|(r, w)| (r.map(DynReader::$name), w.map(DynWriter::$name))),
136 )+
137 }
138 }
139 }
140 };
141}
142
143#[cfg(rustfmt)]
145mod hidraw;
146#[cfg(rustfmt)]
147mod iohidmanager;
148#[cfg(rustfmt)]
149mod win32;
150#[cfg(rustfmt)]
151mod winrt;
152
153dyn_backend_impl! {
156 #[cfg(all(target_os = "windows", feature = "win32"))]
157 mod win32 {
158 Win32(win32::Win32Backend)
159 }
160 #[cfg(all(target_os = "windows", feature = "winrt"))]
161 mod winrt {
162 WinRt(winrt::WinRtBackend)
163 }
164 #[cfg(target_os = "linux")]
165 mod hidraw {
166 HidRaw(hidraw::HidRawBackend)
167 }
168 #[cfg(target_os = "macos")]
169 mod iohidmanager {
170 IoHidManager(iohidmanager::IoHidManagerBackend)
171 }
172}
173
174impl Default for DynBackend {
175 #[allow(unreachable_code)]
176 fn default() -> Self {
177 #[cfg(target_os = "windows")]
178 {
179 #[cfg(feature = "win32")]
180 return Self::new(BackendType::Win32);
181 #[cfg(feature = "winrt")]
182 return Self::new(BackendType::WinRt);
183 }
184 #[cfg(target_os = "linux")]
185 {
186 return Self::new(BackendType::HidRaw);
187 }
188 #[cfg(target_os = "macos")]
189 {
190 return Self::new(BackendType::IoHidManager);
191 }
192 panic!("No suitable backend found");
193 }
194}