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::{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
18 #[allow(clippy::type_complexity)]
19 fn open(&self, id: &DeviceId, read: bool, write: bool) -> impl Future<Output = HidResult<(Option<Self::Reader>, Option<Self::Writer>)>> + Send;
20}
21
22macro_rules! dyn_backend_impl {
23 {
24 $(
25 $(#[$module_attrs:meta])*
26 mod $module:ident {
27 $(#[$item_attrs:meta])*
28 $name:ident($backend:ty)
29 }
30 )+
31 } => {
32 $(
33 $(#[$module_attrs])*
34 mod $module;
35 )+
36
37 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
38 #[non_exhaustive]
39 pub enum BackendType {
40 $(
41 $(#[$module_attrs])*$(#[$item_attrs])*
42 $name,
43 )+
44 }
45
46 pub enum DynReader {
47 $(
48 $(#[$module_attrs])*$(#[$item_attrs])*
49 $name(<$backend as Backend>::Reader),
50 )+
51 }
52 impl AsyncHidRead for DynReader {
53 async fn read_input_report<'a>(&'a mut self, buf: &'a mut [u8]) -> HidResult<usize> {
54 match self {
55 $(
56 $(#[$module_attrs])*$(#[$item_attrs])*
57 Self::$name(i) => i.read_input_report(buf).await,
58 )+
59 }
60 }
61 }
62
63 pub enum DynWriter {
64 $(
65 $(#[$module_attrs])*$(#[$item_attrs])*
66 $name(<$backend as Backend>::Writer),
67 )+
68 }
69 impl AsyncHidWrite for DynWriter {
70 async fn write_output_report<'a>(&'a mut self, buf: &'a [u8]) -> HidResult<()> {
71 match self {
72 $(
73 $(#[$module_attrs])*$(#[$item_attrs])*
74 Self::$name(i) => i.write_output_report(buf).await,
75 )+
76 }
77 }
78 }
79
80 pub enum DynBackend {
81 $(
82 $(#[$module_attrs])*$(#[$item_attrs])*
83 $name($backend),
84 )+
85 }
86 impl DynBackend {
87 pub fn new(backend: BackendType) -> DynBackend {
88 match backend {
89 $(
90 $(#[$module_attrs])*$(#[$item_attrs])*
91 BackendType::$name => Self::$name(<$backend as Default>::default()),
92 )+
93 }
94 }
95 }
96 impl Backend for DynBackend {
97 type Reader = DynReader;
98 type Writer = DynWriter;
99
100
101 async fn enumerate(&self) -> HidResult<DeviceInfoStream> {
102 match self {
103 $(
104 $(#[$module_attrs])*$(#[$item_attrs])*
105 Self::$name(i) => i.enumerate().await,
106 )+
107 }
108 }
109
110 async fn open(&self, id: &DeviceId, read: bool, write: bool) -> HidResult<(Option<Self::Reader>, Option<Self::Writer>)> {
111 match self {
112 $(
113 $(#[$module_attrs])*$(#[$item_attrs])*
114 Self::$name(i) => i.open(id, read, write).await.map(|(r, w)| (r.map(DynReader::$name), w.map(DynWriter::$name))),
115 )+
116 }
117 }
118 }
119 };
120}
121
122dyn_backend_impl! {
125 #[cfg(all(target_os = "windows", feature = "win32"))]
126 mod win32 {
127 Win32(win32::Win32Backend)
128 }
129 #[cfg(all(target_os = "windows", feature = "winrt"))]
130 mod winrt {
131 WinRt(winrt::WinRtBackend)
132 }
133 #[cfg(target_os = "linux")]
134 mod hidraw {
135 HidRaw(hidraw::HidRawBackend)
136 }
137 #[cfg(target_os = "macos")]
138 mod iohidmanager {
139 IoHidManager(iohidmanager::IoHidManagerBackend)
140 }
141}
142
143impl Default for DynBackend {
144 #[allow(unreachable_code)]
145 fn default() -> Self {
146 #[cfg(target_os = "windows")]
147 {
148 #[cfg(feature = "win32")]
149 return Self::new(BackendType::Win32);
150 #[cfg(feature = "winrt")]
151 return Self::new(BackendType::WinRt);
152 }
153 #[cfg(target_os = "linux")]
154 {
155 return Self::new(BackendType::HidRaw);
156 }
157 #[cfg(target_os = "macos")]
158 {
159 return Self::new(BackendType::IoHidManager);
160 }
161 panic!("No suitable backend found");
162 }
163}