acorn_lib/doctor/
mod.rs

1use crate::schema::validate::is_ip6;
2use crate::util::{print_values_as_table, to_string, Label, SemanticVersion};
3use human_units::FormatSize;
4use sysinfo::{Networks, System};
5use which::which;
6
7pub trait TableFormatPrint {
8    fn init() -> Self;
9    fn print(self);
10}
11#[derive(Debug, Clone)]
12pub struct InstalledSoftwareData {
13    pub version: Option<String>,
14    pub path: Option<String>,
15}
16pub struct MemoryInformation {
17    pub total: String,
18    pub available: String,
19    pub used: String,
20    pub swap: String,
21}
22#[derive(Debug, Clone)]
23pub struct Network {
24    pub ip_address: Vec<String>,
25    pub mac_address: String,
26    pub mtu: String,
27}
28#[derive(Debug, Clone)]
29pub struct NetworkInformation {
30    pub networks: Vec<Network>,
31}
32// TODO: Add detailed CPU information printer
33/// Information about microprocessor
34#[derive(Debug, Clone)]
35pub struct Processor {
36    pub frequency: u64,
37    pub vendor: String,
38    pub brand: String,
39}
40pub struct SystemInformation {
41    pub name: String,
42    pub kernel_version: String,
43    pub os_version: String,
44    pub host_name: String,
45    pub cpu_arch: String,
46    pub cpu_count: String,
47}
48pub struct SystemSoftwareInformation {
49    pub acorn: InstalledSoftwareData,
50    pub git: InstalledSoftwareData,
51    pub node: InstalledSoftwareData,
52    pub npm: InstalledSoftwareData,
53    pub npx: InstalledSoftwareData,
54    pub pandoc: InstalledSoftwareData,
55    pub vale: InstalledSoftwareData,
56}
57impl InstalledSoftwareData {
58    fn from_command(name: &str) -> InstalledSoftwareData {
59        InstalledSoftwareData {
60            version: match SemanticVersion::from_command(name) {
61                | Some(version) => Some(version.to_string()),
62                | None => None,
63            },
64            path: match which(name) {
65                | Ok(path) => Some(path.display().to_string()),
66                | Err(_) => None,
67            },
68        }
69    }
70    fn as_row(&self, title: &str) -> Vec<String> {
71        to_string(vec![
72            title,
73            &self.clone().is_installed(),
74            &self.clone().version.unwrap_or_else(|| "---".to_string()),
75            &self.clone().path.unwrap_or_else(|| "---".to_string()),
76        ])
77    }
78    fn is_installed(&self) -> String {
79        if self.version.is_some() {
80            Label::CHECKMARK.to_string()
81        } else {
82            Label::CAUTION.to_string()
83        }
84    }
85}
86impl TableFormatPrint for MemoryInformation {
87    fn init() -> MemoryInformation {
88        let mut sys = System::new_all();
89        sys.refresh_all();
90        MemoryInformation {
91            total: sys.total_memory().format_size().to_string(),
92            available: sys.available_memory().format_size().to_string(),
93            used: sys.used_memory().format_size().to_string(),
94            swap: format!("{} of {}", sys.used_swap().format_size(), sys.total_swap().format_size()),
95        }
96    }
97    fn print(self) {
98        let MemoryInformation {
99            total,
100            available,
101            used,
102            swap,
103        } = self;
104        let headers = vec!["Attribute", "Value"];
105        let rows = vec![
106            to_string(vec!["Total", &total]),
107            to_string(vec!["Available", &available]),
108            to_string(vec!["Used", &used]),
109            to_string(vec!["Swap", &swap]),
110        ];
111        print_values_as_table("Memory", headers, rows);
112    }
113}
114impl TableFormatPrint for NetworkInformation {
115    fn init() -> NetworkInformation {
116        let networks = Networks::new_with_refreshed_list()
117            .into_iter()
118            .map(|(_, network)| Network {
119                ip_address: parse_network_addresses(network),
120                mac_address: network.mac_address().to_string(),
121                mtu: network.mtu().to_string(),
122            })
123            .collect();
124        NetworkInformation { networks }
125    }
126    fn print(self) {
127        let headers = vec!["Network", "MAC Address", "MTU"];
128        let rows = self
129            .networks
130            .iter()
131            .filter_map(|network| {
132                if network.ip_address.is_empty() {
133                    None
134                } else {
135                    let ip = network.ip_address.join("\n");
136                    let mac = network.clone().mac_address;
137                    let mtu = network.clone().mtu;
138                    Some(vec![ip, mac, mtu])
139                }
140            })
141            .collect::<Vec<_>>();
142        print_values_as_table("Network", headers, rows);
143    }
144}
145impl TableFormatPrint for SystemInformation {
146    fn init() -> SystemInformation {
147        let mut sys = System::new_all();
148        sys.refresh_all();
149        SystemInformation {
150            name: System::name().unwrap(),
151            kernel_version: System::kernel_version().unwrap(),
152            os_version: System::os_version().unwrap(),
153            host_name: System::host_name().unwrap(),
154            cpu_arch: System::cpu_arch(),
155            cpu_count: sys.cpus().len().to_string(),
156        }
157    }
158    fn print(self) {
159        let SystemInformation {
160            name,
161            kernel_version,
162            os_version,
163            host_name,
164            cpu_arch,
165            cpu_count,
166        } = self;
167        let headers = vec!["Attribute", "Value"];
168        let rows = vec![
169            to_string(vec!["Name", &name]),
170            to_string(vec!["Kernel Version", &kernel_version]),
171            to_string(vec!["OS Version", &os_version]),
172            to_string(vec!["Host Name", &host_name]),
173            to_string(vec!["CPU Architecture", &cpu_arch]),
174            to_string(vec!["CPU Count", &cpu_count]),
175        ];
176        print_values_as_table("System", headers, rows);
177    }
178}
179impl TableFormatPrint for SystemSoftwareInformation {
180    fn init() -> SystemSoftwareInformation {
181        SystemSoftwareInformation {
182            acorn: InstalledSoftwareData::from_command("acorn"),
183            git: InstalledSoftwareData::from_command("git"),
184            node: InstalledSoftwareData::from_command("node"),
185            npm: InstalledSoftwareData::from_command("npm"),
186            npx: InstalledSoftwareData::from_command("npx"),
187            pandoc: InstalledSoftwareData::from_command("pandoc"),
188            vale: InstalledSoftwareData::from_command("vale"),
189        }
190    }
191    fn print(self) {
192        let SystemSoftwareInformation {
193            acorn,
194            git,
195            node,
196            npm,
197            npx,
198            pandoc,
199            vale,
200        } = self;
201        let headers = vec!["Name", "Installed", "Version", "Location"];
202        let rows = vec![
203            acorn.as_row("Acorn"),
204            git.as_row("Git"),
205            node.as_row("Node.js"),
206            npm.as_row("npm"),
207            npx.as_row("npx"),
208            pandoc.as_row("Pandoc"),
209            vale.as_row("Vale"),
210        ];
211        print_values_as_table("Software", headers, rows);
212    }
213}
214pub fn print_system_information() {
215    SystemInformation::init().print();
216    NetworkInformation::init().print();
217    MemoryInformation::init().print();
218}
219fn parse_network_addresses(data: &sysinfo::NetworkData) -> Vec<String> {
220    data.ip_networks()
221        .iter()
222        .map(|ip| ip.addr.to_string())
223        .filter(|x| is_ip6(x).is_err())
224        .collect::<Vec<String>>()
225}