#[cfg(not(target_os = "linux"))]
core::compile_error!("unsupported platform");
use std::collections::HashSet;
use std::fs;
use std::io;
use std::path::PathBuf;
use crate::Device;
const SYSFS_PATH: &str = if cfg!(test) {
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/sys")
} else {
"/sys"
};
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Scanner {
devices: HashSet<Device>,
}
impl Scanner {
pub fn scan() -> io::Result<Vec<Device>> {
let mut scanner = Scanner::default();
scanner.scan_bus()?;
scanner.scan_class()?;
scanner.scan_block()?;
let devices = scanner.devices.into_iter().collect();
Ok(devices)
}
fn scan_bus(&mut self) -> io::Result<()> {
let path: PathBuf = [SYSFS_PATH, "bus"].iter().collect();
for subsys in fs::read_dir(path)? {
let devices = subsys?.path().join("devices");
for device in fs::read_dir(&devices)? {
let device_link = device?.path().read_link()?;
let device_path = devices.join(device_link).canonicalize()?;
self.devices.insert(device_path.into());
}
}
Ok(())
}
fn scan_class(&mut self) -> io::Result<()> {
let path: PathBuf = [SYSFS_PATH, "class"].iter().collect();
for class in fs::read_dir(path)? {
let devices = class?.path();
for device in fs::read_dir(&devices)? {
let device_path = device?.path();
if !device_path.is_symlink() {
continue;
}
let device_link = device_path.read_link()?;
let device_path = devices.join(device_link).canonicalize()?;
self.devices.insert(device_path.into());
}
}
Ok(())
}
fn scan_block(&mut self) -> io::Result<()> {
let path: PathBuf = [SYSFS_PATH, "block"].iter().collect();
for device in fs::read_dir(&path)? {
let device_link = device?.path().read_link()?;
let device_path = path.join(device_link).canonicalize()?;
self.devices.insert(device_path.into());
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use std::collections::BTreeSet;
use super::*;
#[test]
fn scan() {
let devices = Scanner::scan().expect("failed to scan");
let device_paths: BTreeSet<_> = devices.into_iter().map(|dev| dev.sysfs_path).collect();
let root_path: PathBuf = [SYSFS_PATH, "devices"].iter().collect();
let expected_paths: BTreeSet<_> = [
"device_0",
"device_1",
"device_1/device_2",
"device_1/device_2/device_3",
"device_1/device_2/device_4",
"device_5",
"device_6",
"device_7",
]
.into_iter()
.map(|path| root_path.join(path))
.collect();
assert_eq!(device_paths, expected_paths);
}
#[test]
fn parents() {
let devices = Scanner::scan().expect("failed to scan");
let parents: BTreeSet<_> = devices
.iter()
.map(|dev| {
let parent = dev.parent(&devices).map(|parent| parent.sysfs_path.clone());
(dev.sysfs_path.clone(), parent)
})
.collect();
let root_path: PathBuf = [SYSFS_PATH, "devices"].iter().collect();
let expected_parents: BTreeSet<_> = [
("device_0", None),
("device_1", None),
("device_1/device_2", Some("device_1")),
("device_1/device_2/device_3", Some("device_1/device_2")),
("device_1/device_2/device_4", Some("device_1/device_2")),
("device_5", None),
("device_6", None),
("device_7", None),
]
.into_iter()
.map(|(path, parent)| {
let parent = parent.map(|parent| root_path.join(parent));
(root_path.join(path), parent)
})
.collect();
assert_eq!(parents, expected_parents);
}
#[test]
fn descendants() {
let devices = Scanner::scan().expect("failed to scan");
let descendants: BTreeSet<_> = devices
.iter()
.map(|dev| {
let descendants = dev
.descendants(&devices)
.map(|descendant| descendant.sysfs_path.clone())
.collect::<BTreeSet<_>>();
(dev.sysfs_path.clone(), descendants)
})
.collect();
let root_path: PathBuf = [SYSFS_PATH, "devices"].iter().collect();
let expected_descendants: BTreeSet<_> = [
("device_0", vec![]),
(
"device_1",
vec![
"device_1/device_2",
"device_1/device_2/device_3",
"device_1/device_2/device_4",
],
),
(
"device_1/device_2",
vec!["device_1/device_2/device_3", "device_1/device_2/device_4"],
),
("device_1/device_2/device_3", vec![]),
("device_1/device_2/device_4", vec![]),
("device_5", vec![]),
("device_6", vec![]),
("device_7", vec![]),
]
.into_iter()
.map(|(path, descendants)| {
let descendants = descendants
.into_iter()
.map(|descendant| root_path.join(descendant))
.collect::<BTreeSet<_>>();
(root_path.join(path), descendants)
})
.collect();
assert_eq!(descendants, expected_descendants);
}
}