udev/
enumerator.rs
1use std::ffi::OsStr;
2use std::io::Result;
3use std::marker::PhantomData;
4use std::path::Path;
5
6use Udev;
7use {ffi, list::List, util};
8
9use {AsRaw, AsRawWithContext, Device, FromRaw};
10
11pub struct Enumerator {
17 udev: Udev,
18 enumerator: *mut ffi::udev_enumerate,
19}
20
21impl Clone for Enumerator {
22 fn clone(&self) -> Self {
23 Self {
24 udev: self.udev.clone(),
25 enumerator: unsafe { ffi::udev_enumerate_ref(self.enumerator) },
26 }
27 }
28}
29
30impl Drop for Enumerator {
31 fn drop(&mut self) {
32 unsafe { ffi::udev_enumerate_unref(self.enumerator) };
33 }
34}
35
36#[cfg(feature = "send")]
37unsafe impl Send for Enumerator {}
38#[cfg(feature = "sync")]
39unsafe impl Sync for Enumerator {}
40
41as_ffi_with_context!(
42 Enumerator,
43 enumerator,
44 ffi::udev_enumerate,
45 ffi::udev_enumerate_ref
46);
47
48impl Enumerator {
49 pub fn new() -> Result<Self> {
51 let udev = Udev::new()?;
53 Self::with_udev(udev)
54 }
55
56 pub fn with_udev(udev: Udev) -> Result<Self> {
58 let ptr = try_alloc!(unsafe { ffi::udev_enumerate_new(udev.as_raw()) });
59 Ok(Self {
60 udev,
61 enumerator: ptr,
62 })
63 }
64
65 pub fn match_is_initialized(&mut self) -> Result<()> {
67 util::errno_to_result(unsafe {
68 ffi::udev_enumerate_add_match_is_initialized(self.enumerator)
69 })
70 }
71
72 pub fn match_subsystem<T: AsRef<OsStr>>(&mut self, subsystem: T) -> Result<()> {
74 let subsystem = util::os_str_to_cstring(subsystem)?;
75
76 util::errno_to_result(unsafe {
77 ffi::udev_enumerate_add_match_subsystem(self.enumerator, subsystem.as_ptr())
78 })
79 }
80
81 pub fn match_attribute<T: AsRef<OsStr>, U: AsRef<OsStr>>(
83 &mut self,
84 attribute: T,
85 value: U,
86 ) -> Result<()> {
87 let attribute = util::os_str_to_cstring(attribute)?;
88 let value = util::os_str_to_cstring(value)?;
89
90 util::errno_to_result(unsafe {
91 ffi::udev_enumerate_add_match_sysattr(
92 self.enumerator,
93 attribute.as_ptr(),
94 value.as_ptr(),
95 )
96 })
97 }
98
99 pub fn match_sysname<T: AsRef<OsStr>>(&mut self, sysname: T) -> Result<()> {
101 let sysname = util::os_str_to_cstring(sysname)?;
102
103 util::errno_to_result(unsafe {
104 ffi::udev_enumerate_add_match_sysname(self.enumerator, sysname.as_ptr())
105 })
106 }
107
108 pub fn match_property<T: AsRef<OsStr>, U: AsRef<OsStr>>(
110 &mut self,
111 property: T,
112 value: U,
113 ) -> Result<()> {
114 let property = util::os_str_to_cstring(property)?;
115 let value = util::os_str_to_cstring(value)?;
116
117 util::errno_to_result(unsafe {
118 ffi::udev_enumerate_add_match_property(
119 self.enumerator,
120 property.as_ptr(),
121 value.as_ptr(),
122 )
123 })
124 }
125
126 pub fn match_tag<T: AsRef<OsStr>>(&mut self, tag: T) -> Result<()> {
128 let tag = util::os_str_to_cstring(tag)?;
129
130 util::errno_to_result(unsafe {
131 ffi::udev_enumerate_add_match_tag(self.enumerator, tag.as_ptr())
132 })
133 }
134
135 pub fn match_parent(&mut self, parent: &Device) -> Result<()> {
137 util::errno_to_result(unsafe {
138 ffi::udev_enumerate_add_match_parent(self.enumerator, parent.as_raw())
139 })
140 }
141
142 pub fn nomatch_subsystem<T: AsRef<OsStr>>(&mut self, subsystem: T) -> Result<()> {
144 let subsystem = util::os_str_to_cstring(subsystem)?;
145
146 util::errno_to_result(unsafe {
147 ffi::udev_enumerate_add_nomatch_subsystem(self.enumerator, subsystem.as_ptr())
148 })
149 }
150
151 pub fn nomatch_attribute<T: AsRef<OsStr>, U: AsRef<OsStr>>(
153 &mut self,
154 attribute: T,
155 value: U,
156 ) -> Result<()> {
157 let attribute = util::os_str_to_cstring(attribute)?;
158 let value = util::os_str_to_cstring(value)?;
159
160 util::errno_to_result(unsafe {
161 ffi::udev_enumerate_add_nomatch_sysattr(
162 self.enumerator,
163 attribute.as_ptr(),
164 value.as_ptr(),
165 )
166 })
167 }
168
169 pub fn add_syspath<T: AsRef<OsStr>>(&mut self, syspath: T) -> Result<()> {
171 let syspath = util::os_str_to_cstring(syspath)?;
172
173 util::errno_to_result(unsafe {
174 ffi::udev_enumerate_add_syspath(self.enumerator, syspath.as_ptr())
175 })
176 }
177
178 pub fn scan_devices(&mut self) -> Result<List<Enumerator, Device>> {
182 util::errno_to_result(unsafe { ffi::udev_enumerate_scan_devices(self.enumerator) })?;
183
184 Ok(Devices {
185 entry: unsafe { ffi::udev_enumerate_get_list_entry(self.enumerator) },
186 phantom: PhantomData,
187 })
188 }
189}
190
191pub type Devices<'a> = List<'a, Enumerator, Device>;
193
194impl<'a> Iterator for Devices<'a> {
195 type Item = Device;
196
197 fn next(&mut self) -> Option<Device> {
198 while !self.entry.is_null() {
199 let syspath = Path::new(unsafe {
200 util::ptr_to_os_str_unchecked(ffi::udev_list_entry_get_name(self.entry))
201 });
202
203 self.entry = unsafe { ffi::udev_list_entry_get_next(self.entry) };
204
205 match Device::from_syspath(syspath) {
206 Ok(d) => return Some(d),
207 Err(_) => continue,
208 };
209 }
210
211 None
212 }
213
214 fn size_hint(&self) -> (usize, Option<usize>) {
215 (0, None)
216 }
217}
218
219#[cfg(test)]
220mod tests {
221 use super::*;
222 use crate::{AsRawWithContext, FromRawWithContext};
223
224 #[test]
225 fn create_enumerator() {
226 Enumerator::new().unwrap();
227 }
228
229 #[test]
230 fn round_trip_to_raw_pointers() {
231 let enumerator = Enumerator::new().unwrap();
232
233 let (udev, ptr) = enumerator.into_raw_with_context();
235
236 let mut enumerator = unsafe { Enumerator::from_raw_with_context(udev, ptr) };
237
238 let _ = enumerator.scan_devices().unwrap().collect::<Vec<_>>();
240 }
241
242 #[test]
243 fn test_enumeration() {
244 fn find_hidraws(en: &mut Enumerator) -> Devices<'_> {
245 en.match_is_initialized().unwrap();
246 en.match_subsystem("hidraw").unwrap();
247 en.scan_devices().unwrap()
248 }
249
250 let mut en = Enumerator::new().unwrap();
251 for dev in find_hidraws(&mut en) {
252 println!("Found a hidraw at {:?}", dev.devnode());
253 }
254 }
255
256 #[test]
263 fn test_enumerate_all() {
264 let mut en = Enumerator::new().unwrap();
265
266 for dev in en.scan_devices().unwrap() {
267 println!("Found a device at {:?}", dev.devnode());
268 }
269 }
270}