use crate::detect::{Row, Rows};
pub fn detect() -> Rows {
let mut rows = nvidia_gpus();
if rows.is_empty() {
rows = lspci_gpus();
}
rows
}
fn nvidia_gpus() -> Rows {
let mut rows = Vec::new();
let Ok(entries) = std::fs::read_dir("/proc/driver/nvidia/gpus") else {
return rows;
};
for entry in entries.flatten() {
let info = entry.path().join("information");
let Some(path) = info.to_str() else { continue };
let Some(text) = crate::util::read_trim(path) else {
continue;
};
for line in text.lines() {
if let Some((k, v)) = line.split_once(':') {
if k.trim() == "Model" {
let model = v.trim();
if !model.is_empty() {
rows.push(Row::val(model));
}
break;
}
}
}
}
rows
}
fn lspci_gpus() -> Rows {
let Some(out) = crate::util::cmd("lspci", &[]) else {
return Vec::new();
};
let mut rows = Vec::new();
for line in out.lines() {
if !(line.contains("VGA compatible controller")
|| line.contains("3D controller")
|| line.contains("Display controller"))
{
continue;
}
let Some((_, desc)) = line.rsplit_once(": ") else {
continue;
};
let name = clean_lspci(desc.trim());
if !name.is_empty() {
rows.push(Row::val(name));
}
}
rows
}
fn clean_lspci(desc: &str) -> String {
let stripped = desc
.strip_prefix("NVIDIA Corporation ")
.or_else(|| desc.strip_prefix("Intel Corporation "))
.or_else(|| desc.strip_prefix("Advanced Micro Devices, Inc. "))
.unwrap_or(desc);
if let Some(start) = stripped.find('[') {
if let Some(end) = stripped[start + 1..].find(']') {
let inner = stripped[start + 1..start + 1 + end].trim();
if !inner.is_empty() {
return inner.to_string();
}
}
}
stripped.trim().to_string()
}