use crate::{Icon, IconMode, virt};
use std::{
fs::File,
io::{BufRead as _, BufReader},
path::Path,
};
#[derive(Copy, Clone)]
#[non_exhaustive]
pub enum Chassis {
Desktop,
Server,
Laptop,
Convertible,
Tablet,
Handset,
Watch,
Embedded,
Virtual,
Container,
Unknown,
}
impl From<&str> for Chassis {
fn from(s: &str) -> Chassis {
match s {
"desktop" => Chassis::Desktop,
"server" => Chassis::Server,
"laptop" => Chassis::Laptop,
"convertible" => Chassis::Convertible,
"tablet" => Chassis::Tablet,
"handset" => Chassis::Handset,
"watch" => Chassis::Watch,
"embedded" => Chassis::Embedded,
"vm" => Chassis::Virtual,
"container" => Chassis::Container,
_ => Chassis::Unknown,
}
}
}
impl Icon for Chassis {
fn icon(&self, mode: IconMode) -> &'static str {
use IconMode::*;
match self {
Self::Desktop => match mode {
Text => "Desk",
Icons | MinimalIcons => " ",
},
Self::Server => match mode {
Text => "Serv",
Icons | MinimalIcons => " ",
},
Self::Laptop => match mode {
Text => "Lapt",
Icons | MinimalIcons => " ",
},
Self::Convertible => match mode {
Text => "Conv",
Icons | MinimalIcons => " ", },
Self::Tablet => match mode {
Text => "Tabl",
Icons | MinimalIcons => " ",
},
Self::Handset => match mode {
Text => "Hand",
Icons | MinimalIcons => " ",
},
Self::Watch => match mode {
Text => "Watch",
Icons | MinimalIcons => " ",
},
Self::Embedded => match mode {
Text => "Emb",
Icons | MinimalIcons => " ",
},
Self::Virtual => match mode {
Text => "Virt",
Icons | MinimalIcons => " ",
},
Self::Container => match mode {
Text => "Cont",
Icons | MinimalIcons => " ",
},
Self::Unknown => match mode {
Text => "Unkn",
Icons | MinimalIcons => "??", },
}
}
}
impl Chassis {
pub fn get() -> Chassis {
[
Chassis::try_machine_info,
Chassis::try_container,
Chassis::try_udev,
Chassis::try_virtualization,
Chassis::try_dmi_chassis_type,
Chassis::try_acpi_pm_profile,
Chassis::try_device_tree_type,
]
.iter()
.find_map(|f| f())
.unwrap_or(Chassis::Unknown)
}
fn try_machine_info() -> Option<Chassis> {
BufReader::new(File::open("/etc/machine-info").ok()?)
.lines()
.map_while(Result::ok)
.find_map(|line| {
Some(Chassis::from(
line.trim_start()
.strip_prefix("CHASSIS")?
.trim_start()
.strip_prefix('=')?
.trim(),
))
})
}
fn try_udev() -> Option<Chassis> {
BufReader::new(File::open("/run/udev/data/+dmi:id").ok()?)
.lines()
.find_map(|x| Some(Chassis::from(x.ok()?.strip_prefix("E:ID_CHASSIS=")?.trim())))
}
fn try_virtualization() -> Option<Chassis> {
virt::detect_vm().then_some(Chassis::Virtual)
}
fn try_container() -> Option<Chassis> {
virt::detect_container().then_some(Chassis::Container)
}
fn try_dmi_chassis_type() -> Option<Chassis> {
Some(match read_single_u32("/sys/class/dmi/id/chassis_type")? {
0x03 | 0x04 | 0x06 | 0x07 | 0x0d | 0x23 | 0x24 => Chassis::Desktop,
0x08 | 0x09 | 0x0a | 0x0e => Chassis::Laptop,
0x0b => Chassis::Handset,
0x11 | 0x1c | 0x1d => Chassis::Server,
0x1e => Chassis::Tablet,
0x1f | 0x20 => Chassis::Convertible,
0x21 | 0x22 => Chassis::Embedded,
_ => Chassis::Unknown,
})
}
fn try_acpi_pm_profile() -> Option<Chassis> {
Some(match read_single_u32("/sys/firmware/acpi/pm_profile")? {
1 | 3 | 6 => Chassis::Desktop,
2 => Chassis::Laptop,
4 | 5 | 7 => Chassis::Server,
8 => Chassis::Tablet,
_ => Chassis::Unknown,
})
}
fn try_device_tree_type() -> Option<Chassis> {
Some(Chassis::from(
&*std::fs::read_to_string("/proc/device-tree/chassis-type").ok()?,
))
}
}
fn read_single_u32<T: AsRef<Path> + ?Sized>(path: &T) -> Option<u32> {
std::fs::read_to_string(path)
.ok()?
.trim()
.parse::<u32>()
.ok()
}