use eyre::{Result, format_err}; use tokio::process::Command; #[derive(Debug, serde::Deserialize, serde::Serialize)] struct Report { report: Vec, } #[derive(Debug, serde::Deserialize, serde::Serialize)] enum ReportObj { #[serde(rename = "pv")] PV(Vec), #[serde(rename = "vg")] VG(Vec), #[serde(rename = "lv")] LV(Vec), } #[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] pub struct PV { pub pv_name: String, pub vg_name: String, pub pv_fmt: String, pub pv_attr: String, pub pv_size: String, pub pv_free: String, } #[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] pub struct VG { pub vg_name: String, pub pv_count: String, pub lv_count: String, pub snap_count: String, pub vg_attr: String, pub vg_size: String, pub vg_free: String, } #[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] pub struct LV { pub lv_name: String, pub vg_name: String, pub lv_attr: String, pub lv_size: String, pub pool_lv: String, pub origin: String, pub data_percent: String, pub metadata_percent: String, pub move_pv: String, pub mirror_log: String, pub copy_percent: String, pub convert_lv: String, } impl LV { pub fn equal_name(&self, vg_name: &str, lv_name: &str) -> bool { vg_name == &self.vg_name && lv_name == &self.lv_name } } pub async fn pvs() -> Result> { report_cmd("pvs", |o| match o { ReportObj::PV(pv) => Some(pv), _ => None, }) .await } pub async fn vgs() -> Result> { report_cmd("vgs", |o| match o { ReportObj::VG(vg) => Some(vg), _ => None, }) .await } pub async fn lvs() -> Result> { report_cmd("lvs", |o| match o { ReportObj::LV(lv) => Some(lv), _ => None, }) .await } async fn report_cmd(cmd: &str, find: fn(ReportObj) -> Option>) -> Result> { let output = Command::new(cmd) .arg("--reportformat=json") .output() .await?; if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr.trim_ascii()); return Err(format_err!("{cmd} failed: {}", stderr)); } let report: Report = serde_json::from_slice(&output.stdout).unwrap(); Ok((report.report.into_iter()) .filter_map(find) .flatten() .collect()) }