use std::ffi::OsStr;
use std::io::Result;
use std::marker::PhantomData;
use std::path::Path;
use Udev;
use {ffi, list::List, util};
use {AsRaw, AsRawWithContext, Device, FromRaw};
pub struct Enumerator {
udev: Udev,
enumerator: *mut ffi::udev_enumerate,
}
impl Clone for Enumerator {
fn clone(&self) -> Self {
Self {
udev: self.udev.clone(),
enumerator: unsafe { ffi::udev_enumerate_ref(self.enumerator) },
}
}
}
impl Drop for Enumerator {
fn drop(&mut self) {
unsafe { ffi::udev_enumerate_unref(self.enumerator) };
}
}
as_ffi_with_context!(
Enumerator,
enumerator,
ffi::udev_enumerate,
ffi::udev_enumerate_ref
);
impl Enumerator {
pub fn new() -> Result<Self> {
let udev = Udev::new()?;
Self::with_udev(udev)
}
pub fn with_udev(udev: Udev) -> Result<Self> {
let ptr = try_alloc!(unsafe { ffi::udev_enumerate_new(udev.as_raw()) });
Ok(Self {
udev,
enumerator: ptr,
})
}
pub fn match_is_initialized(&mut self) -> Result<()> {
util::errno_to_result(unsafe {
ffi::udev_enumerate_add_match_is_initialized(self.enumerator)
})
}
pub fn match_subsystem<T: AsRef<OsStr>>(&mut self, subsystem: T) -> Result<()> {
let subsystem = util::os_str_to_cstring(subsystem)?;
util::errno_to_result(unsafe {
ffi::udev_enumerate_add_match_subsystem(self.enumerator, subsystem.as_ptr())
})
}
pub fn match_attribute<T: AsRef<OsStr>, U: AsRef<OsStr>>(
&mut self,
attribute: T,
value: U,
) -> Result<()> {
let attribute = util::os_str_to_cstring(attribute)?;
let value = util::os_str_to_cstring(value)?;
util::errno_to_result(unsafe {
ffi::udev_enumerate_add_match_sysattr(
self.enumerator,
attribute.as_ptr(),
value.as_ptr(),
)
})
}
pub fn match_sysname<T: AsRef<OsStr>>(&mut self, sysname: T) -> Result<()> {
let sysname = util::os_str_to_cstring(sysname)?;
util::errno_to_result(unsafe {
ffi::udev_enumerate_add_match_sysname(self.enumerator, sysname.as_ptr())
})
}
pub fn match_property<T: AsRef<OsStr>, U: AsRef<OsStr>>(
&mut self,
property: T,
value: U,
) -> Result<()> {
let property = util::os_str_to_cstring(property)?;
let value = util::os_str_to_cstring(value)?;
util::errno_to_result(unsafe {
ffi::udev_enumerate_add_match_property(
self.enumerator,
property.as_ptr(),
value.as_ptr(),
)
})
}
pub fn match_tag<T: AsRef<OsStr>>(&mut self, tag: T) -> Result<()> {
let tag = util::os_str_to_cstring(tag)?;
util::errno_to_result(unsafe {
ffi::udev_enumerate_add_match_tag(self.enumerator, tag.as_ptr())
})
}
pub fn match_parent(&mut self, parent: &Device) -> Result<()> {
util::errno_to_result(unsafe {
ffi::udev_enumerate_add_match_parent(self.enumerator, parent.as_raw())
})
}
pub fn nomatch_subsystem<T: AsRef<OsStr>>(&mut self, subsystem: T) -> Result<()> {
let subsystem = util::os_str_to_cstring(subsystem)?;
util::errno_to_result(unsafe {
ffi::udev_enumerate_add_nomatch_subsystem(self.enumerator, subsystem.as_ptr())
})
}
pub fn nomatch_attribute<T: AsRef<OsStr>, U: AsRef<OsStr>>(
&mut self,
attribute: T,
value: U,
) -> Result<()> {
let attribute = util::os_str_to_cstring(attribute)?;
let value = util::os_str_to_cstring(value)?;
util::errno_to_result(unsafe {
ffi::udev_enumerate_add_nomatch_sysattr(
self.enumerator,
attribute.as_ptr(),
value.as_ptr(),
)
})
}
pub fn add_syspath<T: AsRef<OsStr>>(&mut self, syspath: T) -> Result<()> {
let syspath = util::os_str_to_cstring(syspath)?;
util::errno_to_result(unsafe {
ffi::udev_enumerate_add_syspath(self.enumerator, syspath.as_ptr())
})
}
pub fn scan_devices(&mut self) -> Result<List<Enumerator, Device>> {
util::errno_to_result(unsafe { ffi::udev_enumerate_scan_devices(self.enumerator) })?;
Ok(Devices {
entry: unsafe { ffi::udev_enumerate_get_list_entry(self.enumerator) },
phantom: PhantomData,
})
}
}
pub type Devices<'a> = List<'a, Enumerator, Device>;
impl<'a> Iterator for Devices<'a> {
type Item = Device;
fn next(&mut self) -> Option<Device> {
while !self.entry.is_null() {
let syspath = Path::new(unsafe {
util::ptr_to_os_str_unchecked(ffi::udev_list_entry_get_name(self.entry))
});
self.entry = unsafe { ffi::udev_list_entry_get_next(self.entry) };
match Device::from_syspath(syspath) {
Ok(d) => return Some(d),
Err(_) => continue,
};
}
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, None)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{AsRawWithContext, FromRawWithContext};
#[test]
fn create_enumerator() {
Enumerator::new().unwrap();
}
#[test]
fn round_trip_to_raw_pointers() {
let enumerator = Enumerator::new().unwrap();
let (udev, ptr) = enumerator.into_raw_with_context();
let mut enumerator = unsafe { Enumerator::from_raw_with_context(udev, ptr) };
let _ = enumerator.scan_devices().unwrap().collect::<Vec<_>>();
}
#[test]
fn test_enumeration() {
fn find_hidraws(en: &mut Enumerator) -> Devices<'_> {
en.match_is_initialized().unwrap();
en.match_subsystem("hidraw").unwrap();
en.scan_devices().unwrap()
}
let mut en = Enumerator::new().unwrap();
for dev in find_hidraws(&mut en) {
println!("Found a hidraw at {:?}", dev.devnode());
}
}
#[test]
fn test_enumerate_all() {
let mut en = Enumerator::new().unwrap();
for dev in en.scan_devices().unwrap() {
println!("Found a device at {:?}", dev.devnode());
}
}
}