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::{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// Rustfmt doesn't like my macro so we just declare them all with a bogus cfg attribute
144#[cfg(rustfmt)]
145mod hidraw;
146#[cfg(rustfmt)]
147mod iohidmanager;
148#[cfg(rustfmt)]
149mod win32;
150#[cfg(rustfmt)]
151mod winrt;
152
153// Dynamic dispatch doesn't play well with async traits so we just generate a big enum
154// that forwards function calls the correct implementations
155dyn_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}