Skip to main content

async_hid/backend/
mod.rs

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::{AsyncHidFeatureHandle, 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    type FeatureHandle: AsyncHidFeatureHandle + Send + Sync;
16
17    fn enumerate(&self) -> impl Future<Output = HidResult<DeviceInfoStream>> + Send;
18    fn watch(&self) -> HidResult<Boxed<DeviceEvent>>;
19
20    fn query_info(&self, id: &DeviceId) -> impl Future<Output = HidResult<Vec<DeviceInfo>>> + Send;
21
22    #[allow(clippy::type_complexity)]
23    fn open(&self, id: &DeviceId, read: bool, write: bool) -> impl Future<Output = HidResult<(Option<Self::Reader>, Option<Self::Writer>)>> + Send;
24    fn open_feature_handle(&self, id: &DeviceId) -> impl Future<Output = HidResult<Self::FeatureHandle>> + Send;
25
26    async fn read_feature_report(&self, id: &DeviceId, buf: &mut [u8]) -> HidResult<usize> {
27        let mut feature_buffer = self.open_feature_handle(id).await?;
28        feature_buffer.read_feature_report(buf).await
29    }
30}
31
32macro_rules! dyn_backend_impl {
33    {
34        $(
35            $(#[$module_attrs:meta])*
36            mod $module:ident {
37                $(#[$item_attrs:meta])*
38                $name:ident($backend:ty)
39            }
40        )+
41    } => {
42        $(
43            $(#[$module_attrs])*
44            mod $module;
45        )+
46
47        #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
48        #[non_exhaustive]
49        pub enum BackendType {
50            $(
51                $(#[$module_attrs])*$(#[$item_attrs])*
52                $name,
53            )+
54        }
55
56        pub enum DynReader {
57            $(
58                $(#[$module_attrs])*$(#[$item_attrs])*
59                $name(<$backend as Backend>::Reader),
60            )+
61        }
62        impl AsyncHidRead for DynReader {
63            async fn read_input_report<'a>(&'a mut self, buf: &'a mut [u8]) -> HidResult<usize> {
64                match self {
65                    $(
66                        $(#[$module_attrs])*$(#[$item_attrs])*
67                        Self::$name(i) => i.read_input_report(buf).await,
68                    )+
69                }
70            }
71        }
72
73        pub enum DynWriter {
74            $(
75                $(#[$module_attrs])*$(#[$item_attrs])*
76                $name(<$backend as Backend>::Writer),
77            )+
78        }
79        impl AsyncHidWrite for DynWriter {
80            async fn write_output_report<'a>(&'a mut self, buf: &'a [u8]) -> HidResult<()> {
81                match self {
82                    $(
83                        $(#[$module_attrs])*$(#[$item_attrs])*
84                        Self::$name(i) => i.write_output_report(buf).await,
85                    )+
86                }
87            }
88        }
89
90        pub enum DynFeatureHandle {
91            $(
92                $(#[$module_attrs])*$(#[$item_attrs])*
93                $name(<$backend as Backend>::FeatureHandle),
94            )+
95        }
96        impl AsyncHidFeatureHandle for DynFeatureHandle {
97            async fn read_feature_report<'a>(&'a mut self, buf: &'a mut [u8]) -> HidResult<usize> {
98                match self {
99                    $(
100                        $(#[$module_attrs])*$(#[$item_attrs])*
101                        Self::$name(i) => i.read_feature_report(buf).await,
102                    )+
103                }
104            }
105
106            async fn write_feature_report<'a>(&'a mut self, buf: &'a [u8]) -> HidResult<()> {
107                match self {
108                    $(
109                        $(#[$module_attrs])*$(#[$item_attrs])*
110                        Self::$name(i) => i.write_feature_report(buf).await,
111                    )+
112                }
113            }
114        }
115
116         pub enum DynBackend {
117            $(
118                $(#[$module_attrs])*$(#[$item_attrs])*
119                $name($backend),
120            )+
121        }
122        impl DynBackend {
123            pub fn new(backend: BackendType) -> DynBackend {
124                match backend {
125                    $(
126                        $(#[$module_attrs])*$(#[$item_attrs])*
127                        BackendType::$name => Self::$name(<$backend as Default>::default()),
128                    )+
129                }
130            }
131        }
132        impl Backend for DynBackend {
133            type Reader = DynReader;
134            type Writer = DynWriter;
135            type FeatureHandle = DynFeatureHandle;
136
137            async fn enumerate(&self) -> HidResult<DeviceInfoStream> {
138                match self {
139                    $(
140                        $(#[$module_attrs])*$(#[$item_attrs])*
141                        Self::$name(i) => i.enumerate().await,
142                    )+
143                }
144            }
145
146            fn watch(&self) -> HidResult<Boxed<DeviceEvent>> {
147                match self {
148                    $(
149                        $(#[$module_attrs])*$(#[$item_attrs])*
150                        Self::$name(i) => i.watch(),
151                    )+
152                }
153            }
154
155             async fn query_info(&self, id: &DeviceId) -> HidResult<Vec<DeviceInfo>> {
156                match self {
157                    $(
158                        $(#[$module_attrs])*$(#[$item_attrs])*
159                        Self::$name(i) => i.query_info(id).await,
160                    )+
161                }
162            }
163
164            async fn open(&self, id: &DeviceId, read: bool, write: bool) -> HidResult<(Option<Self::Reader>, Option<Self::Writer>)> {
165                match self {
166                    $(
167                        $(#[$module_attrs])*$(#[$item_attrs])*
168                        Self::$name(i) => i.open(id, read, write).await.map(|(r, w)| (r.map(DynReader::$name), w.map(DynWriter::$name))),
169                    )+
170                }
171            }
172
173            async fn open_feature_handle(&self, id: &DeviceId) -> HidResult<Self::FeatureHandle> {
174                match self {
175                    $(
176                        $(#[$module_attrs])*$(#[$item_attrs])*
177                        Self::$name(i) => i.open_feature_handle(id).await.map(DynFeatureHandle::$name),
178                    )+
179                }
180            }
181        }
182    };
183}
184
185// Rustfmt doesn't like my macro so we just declare them all with a bogus cfg attribute
186#[cfg(rustfmt)]
187mod hidraw;
188#[cfg(rustfmt)]
189mod iohidmanager;
190#[cfg(rustfmt)]
191mod win32;
192#[cfg(rustfmt)]
193mod winrt;
194
195// Dynamic dispatch doesn't play well with async traits so we just generate a big enum
196// that forwards function calls the correct implementations
197dyn_backend_impl! {
198    #[cfg(all(target_os = "windows", feature = "win32"))]
199    mod win32 {
200        Win32(win32::Win32Backend)
201    }
202    #[cfg(all(target_os = "windows", feature = "winrt"))]
203    mod winrt {
204        WinRt(winrt::WinRtBackend)
205    }
206    #[cfg(target_os = "linux")]
207    mod hidraw {
208        HidRaw(hidraw::HidRawBackend)
209    }
210    #[cfg(target_os = "macos")]
211    mod iohidmanager {
212        IoHidManager(iohidmanager::IoHidManagerBackend)
213    }
214}
215
216impl Default for DynBackend {
217    #[allow(unreachable_code)]
218    fn default() -> Self {
219        #[cfg(target_os = "windows")]
220        {
221            #[cfg(feature = "win32")]
222            return Self::new(BackendType::Win32);
223            #[cfg(feature = "winrt")]
224            return Self::new(BackendType::WinRt);
225        }
226        #[cfg(target_os = "linux")]
227        {
228            return Self::new(BackendType::HidRaw);
229        }
230        #[cfg(target_os = "macos")]
231        {
232            return Self::new(BackendType::IoHidManager);
233        }
234        panic!("No suitable backend found");
235    }
236}