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::{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
122// Dynamic dispatch doesn't play well with async traits so we just generate a big enum
123// that forwards function calls the correct implementations
124dyn_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}