use std::{
fs::File,
io::{self, BufRead},
path::{Path, PathBuf},
};
const POSSIBLE_PCI_IDS_PATHS: [&str; 3] = [
"/usr/share/hwdata/pci.ids",
"/usr/share/misc/pci.ids",
"/var/lib/pciutils/pci.ids",
];
const PCI_IDS_FILE_NOT_FOUND_ERROR: (io::ErrorKind, &str) =
(io::ErrorKind::NotFound, "file pci.ids not found in system");
const PCI_IDENTIFIER_NOT_FOUND_ERROR: (io::ErrorKind, &str) =
(io::ErrorKind::NotFound, "identifier not found");
fn find_pci_ids() -> io::Result<PathBuf> {
for possible_path in POSSIBLE_PCI_IDS_PATHS {
if Path::new(possible_path).is_file() {
return Ok(PathBuf::from(possible_path));
}
}
Err(io::Error::new(
PCI_IDS_FILE_NOT_FOUND_ERROR.0,
PCI_IDS_FILE_NOT_FOUND_ERROR.1,
))
}
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where
P: AsRef<Path>,
{
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
}
fn extract_model_from_line(line: &str, model_buf: &mut String, splitter: &str) {
*model_buf = line
.split(splitter)
.nth(1)
.unwrap_or_default()
.trim()
.to_string();
}
pub fn lookup_for_pci_id_value(pci_id: &str) -> io::Result<String> {
let pci_ids_file_path = find_pci_ids()?;
let pieces = pci_id
.trim()
.to_ascii_lowercase()
.replace(":", " ")
.splitn(3, " ")
.filter(|s| !s.is_empty())
.map(String::from)
.collect::<Vec<String>>();
let pieces_len = pieces.len();
let (mut vendor, mut device, mut _subsys) = (false, false, false);
let mut latest_known_model = String::default();
let (vendor_must_start_with, device_must_start_with, subsys_must_start_with) = (
pieces.first().cloned().unwrap_or_default().to_string(),
format!("\t{}", pieces.get(1).cloned().unwrap_or_default()),
format!("\t\t{}", pieces.get(2).cloned().unwrap_or_default()),
);
if let Ok(lines) = read_lines(pci_ids_file_path) {
for line in lines.flatten() {
if pieces_len >= 1 && !vendor && line.starts_with(&vendor_must_start_with) {
vendor = true;
extract_model_from_line(&line, &mut latest_known_model, &vendor_must_start_with);
} else if pieces_len >= 2
&& (!device && vendor)
&& line.starts_with(&device_must_start_with)
{
device = true;
extract_model_from_line(&line, &mut latest_known_model, &device_must_start_with);
} else if pieces_len == 3
&& (!_subsys && vendor && device)
&& line.starts_with(&subsys_must_start_with)
{
_subsys = true;
extract_model_from_line(&line, &mut latest_known_model, &subsys_must_start_with);
break;
}
if [vendor, device, _subsys].iter().filter(|b| **b).count() == pieces_len {
break;
}
}
}
if latest_known_model.is_empty() {
return Err(io::Error::new(
PCI_IDENTIFIER_NOT_FOUND_ERROR.0,
PCI_IDENTIFIER_NOT_FOUND_ERROR.1,
));
}
Ok(latest_known_model)
}