leenfetch_core/modules/linux/system/
model.rs

1use std::fs;
2
3pub fn get_model() -> Option<String> {
4    // Try Android first
5    if std::path::Path::new("/system/app/").exists()
6        && std::path::Path::new("/system/priv-app/").exists()
7    {
8        return Some("Android Device".to_string()); // Placeholder: no getprop support in pure Rust
9    }
10
11    let paths = [
12        (
13            "/sys/devices/virtual/dmi/id/board_vendor",
14            "/sys/devices/virtual/dmi/id/board_name",
15        ),
16        (
17            "/sys/devices/virtual/dmi/id/product_name",
18            "/sys/devices/virtual/dmi/id/product_version",
19        ),
20        ("/sys/firmware/devicetree/base/model", ""),
21        ("/tmp/sysinfo/model", ""),
22    ];
23
24    let mut model = String::new();
25
26    for (file1, file2) in paths {
27        if std::path::Path::new(file1).exists() {
28            model.push_str(&read_first_line(file1).unwrap_or_default());
29            if !file2.is_empty() && std::path::Path::new(file2).exists() {
30                model.push(' ');
31                model.push_str(&read_first_line(file2).unwrap_or_default());
32            }
33            break;
34        }
35    }
36
37    if model.is_empty() {
38        return None;
39    }
40
41    let cleaned = cleanup_model_string(&model);
42    Some(cleaned)
43}
44
45// Read first line of a file
46fn read_first_line<P: AsRef<std::path::Path>>(path: P) -> Option<String> {
47    fs::read_to_string(path)
48        .ok()
49        .and_then(|s| s.lines().next().map(|line| line.trim().to_string()))
50}
51
52// Clean up known garbage strings
53fn cleanup_model_string(model: &str) -> String {
54    let mut s = model.to_string();
55
56    let garbage = [
57        "To be filled by O.E.M.",
58        "To Be Filled",
59        "OEM",
60        "Not Applicable",
61        "System Product Name",
62        "System Version",
63        "Undefined",
64        "Default string",
65        "Not Specified",
66        "Type1ProductConfigId",
67        "INVALID",
68        "All Series",
69        "�",
70    ];
71
72    for g in garbage {
73        s = s.replace(g, "").trim().to_string();
74    }
75
76    if s.starts_with("Standard PC") {
77        s = format!("KVM/QEMU ({})", model.trim());
78    } else if s.starts_with("OpenBSD") {
79        s = format!("vmm ({})", model.trim());
80    }
81
82    s
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn test_cleanup_garbage() {
91        let raw = "To be filled by O.E.M.";
92        let cleaned = cleanup_model_string(raw);
93        assert_eq!(cleaned, "");
94
95        let raw = "Standard PC (i440FX)";
96        let cleaned = cleanup_model_string(raw);
97        assert!(cleaned.contains("KVM/QEMU"));
98    }
99
100    #[test]
101    fn test_model_maybe_exists() {
102        let model = get_model();
103        assert!(model.is_none() || !model.as_ref().unwrap().is_empty());
104    }
105}