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#[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}