use std::path::Path;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MediaType {
SSD,
HDD,
Unknown,
}
pub struct MediaDetector;
impl MediaDetector {
pub fn new() -> Self {
Self
}
pub fn detect<P: AsRef<Path>>(&self, path: P) -> MediaType {
#[cfg(target_os = "linux")]
{
self.detect_linux(path.as_ref())
}
#[cfg(not(target_os = "linux"))]
{
MediaType::Unknown }
}
#[cfg(target_os = "linux")]
fn detect_linux(&self, path: &Path) -> MediaType {
let device_path = match self.get_device_path(path) {
Some(dev) => dev,
None => return MediaType::Unknown,
};
let rotational_path = format!("/sys/block/{}/queue/rotational", device_path);
if let Ok(contents) = std::fs::read_to_string(&rotational_path) {
if contents.trim() == "0" {
MediaType::SSD
} else {
MediaType::HDD
}
} else {
MediaType::Unknown
}
}
#[cfg(target_os = "linux")]
fn get_device_path(&self, path: &Path) -> Option<String> {
use std::os::unix::fs::MetadataExt;
let metadata = std::fs::metadata(path).ok()?;
let dev = metadata.dev();
for entry in std::fs::read_dir("/sys/block").ok()? {
let entry = entry.ok()?;
let device_name = entry.file_name();
let device_str = device_name.to_string_lossy();
if device_str.starts_with("loop") {
continue;
}
let dev_path = format!("/dev/{}", device_str);
if let Ok(dev_metadata) = std::fs::metadata(&dev_path) {
if dev_metadata.rdev() == dev {
return Some(device_str.to_string());
}
}
}
None
}
}
impl Default for MediaDetector {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_media_detector_creation() {
let detector = MediaDetector::new();
let _ = detector.detect("/tmp");
}
#[test]
fn test_media_detector_default() {
let detector = MediaDetector::default();
let media_type = detector.detect("/tmp");
assert!(matches!(
media_type,
MediaType::SSD | MediaType::HDD | MediaType::Unknown
));
}
}