use std::collections::BTreeSet; use imbl_value::InternedString; use serde::{Deserialize, Serialize}; use tokio::process::Command; use ts_rs::TS; use crate::prelude::*; use crate::util::Invoke; const KNOWN_CLASSES: &[&str] = &["processor", "display"]; #[derive(Clone, Debug, Deserialize, Serialize, TS)] #[serde(tag = "class")] #[serde(rename_all = "camelCase")] #[ts(export)] pub enum LshwDevice { Processor(LshwProcessor), Display(LshwDisplay), } impl LshwDevice { pub fn class(&self) -> &'static str { match self { Self::Processor(_) => "processor", Self::Display(_) => "display", } } #[instrument(skip_all)] pub fn from_value(value: &Value) -> Option { match value["class"].as_str() { Some("processor") => Some(LshwDevice::Processor(LshwProcessor::from_value(value))), Some("display") => Some(LshwDevice::Display(LshwDisplay::from_value(value))), _ => None, } } } #[derive(Clone, Debug, Deserialize, Serialize, TS)] pub struct LshwProcessor { pub product: Option, pub vendor: Option, pub capabilities: BTreeSet, } impl LshwProcessor { #[instrument(skip_all)] fn from_value(value: &Value) -> Self { Self { product: value["product"].as_str().map(From::from), vendor: value["vendor"].as_str().map(From::from), capabilities: value["capabilities"] .as_object() .into_iter() .flat_map(|o| o.keys()) .map(|k| k.clone()) .collect(), } } } #[derive(Clone, Debug, Deserialize, Serialize, TS)] pub struct LshwDisplay { pub product: Option, pub vendor: Option, pub capabilities: BTreeSet, pub driver: Option, } impl LshwDisplay { #[instrument(skip_all)] fn from_value(value: &Value) -> Self { Self { product: value["product"].as_str().map(From::from), vendor: value["vendor"].as_str().map(From::from), capabilities: value["capabilities"] .as_object() .into_iter() .flat_map(|o| o.keys()) .map(|k| k.clone()) .collect(), driver: value["configuration"]["driver"].as_str().map(From::from), } } } pub async fn lshw() -> Result, Error> { let mut cmd = Command::new("lshw"); cmd.arg("-json"); for class in KNOWN_CLASSES { cmd.arg("-class").arg(*class); } Ok( serde_json::from_slice::>(&cmd.invoke(crate::ErrorKind::Lshw).await?) .with_kind(crate::ErrorKind::Deserialization)? .iter() .filter_map(LshwDevice::from_value) .collect(), ) }