1#[cfg(feature = "gpu")]
2use gfxinfo::active_gpu;
3use rust_decimal::{
4 Decimal,
5 prelude::{FromPrimitive, ToPrimitive},
6};
7#[cfg(feature = "process")]
8pub use sysinfo::Pid;
9#[cfg(feature = "network")]
10use {
11 std::net::IpAddr,
12 sysinfo::{MacAddr, Networks},
13};
14
15#[derive(Debug, Clone)]
16#[cfg(feature = "host")]
17pub struct HostInfo {
18 pub host_name: String,
20 pub os_name: String,
22 pub os_version: String,
24 pub os_type: String,
26 pub arch: String,
28 pub boot_time: u64,
30 pub uptime: u64,
32}
33
34#[derive(Debug, Clone)]
35#[cfg(feature = "network")]
36pub struct NetworkInfo {
37 pub name: String,
39 pub ip_info: Vec<IpInfo>,
41 pub upload: f64,
43 pub download: f64,
45 pub mac_addr: MacAddr,
47}
48
49#[derive(Debug, Clone)]
50#[cfg(feature = "network")]
51pub struct IpInfo {
52 pub ip_address: IpAddr,
54 pub netmask: u8,
56}
57
58#[derive(Debug, Clone)]
59pub struct ProcessInfo {
60 pub pid: Pid,
62 pub name: String,
64 pub start_time: u64,
66 pub run_time: u64,
68 pub cpu_usage: Option<u8>,
70 pub memory_usage: Option<u8>,
72 pub used_memory: f32,
74}
75#[derive(Debug, Clone)]
76#[cfg(feature = "cpu")]
77pub struct CpuInfo {
78 pub cpu_model: String,
80 pub cpu_cores: usize,
82 pub cpu_frequency: Option<f32>,
84 pub cpu_usage: Option<u8>,
86}
87
88#[derive(Debug, Clone)]
89#[cfg(feature = "gpu")]
90pub struct GpuInfo {
91 pub gpu_model: String,
93 pub gpu_memory_used: f32,
95 pub gpu_memory_total: f32,
97 pub gpu_memory_free: f32,
99 pub gpu_usage: u8,
101}
102
103#[derive(Debug, Clone)]
104#[cfg(feature = "memory")]
105pub struct MemoryInfo {
106 pub total_memory: f32,
108 pub used_memory: f32,
110 pub free_memory: f32,
112 pub swap_memory_total: f32,
114 pub swap_memory_used: f32,
116 pub swap_memory_free: f32,
118 pub swap_memory_usage: Option<u8>,
120 pub memory_usage: Option<u8>,
122}
123
124#[derive(Debug, Clone)]
125#[cfg(feature = "disk")]
126pub struct DiskDetail {
127 pub name: String,
129 pub total_space: f32,
131 pub used_space: f32,
133 pub free_space: f32,
135 pub usage: f32,
137}
138
139#[derive(Debug, Clone)]
140#[cfg(feature = "disk")]
141pub struct DiskInfo {
142 pub total_disk_space: f32,
144 pub total_used_space: f32,
146 pub total_free_space: f32,
148 pub total_usage: f32,
150 pub disks: Vec<DiskDetail>,
152}
153
154#[derive(Debug, Clone)]
155pub struct SystemInfo;
156
157impl SystemInfo {
158 #[cfg(feature = "host")]
166 pub fn host() -> HostInfo {
167 use std::env;
168 use sysinfo::System;
169 let hostname = System::host_name().unwrap();
170 let os_name = System::name().unwrap();
171 let arch = System::cpu_arch();
172 let os_version = System::os_version().unwrap();
173 let os_type = env::consts::OS.to_string();
174 let boot_time = System::boot_time();
175 let uptime = System::uptime();
176 HostInfo { host_name: hostname, os_name, arch, os_version, os_type, boot_time, uptime }
177 }
178
179 #[cfg(feature = "cpu")]
187 pub fn cpu() -> CpuInfo {
188 use std::thread::sleep;
189 use sysinfo::System;
190 let mut system = System::new();
191 system.refresh_cpu_all();
192
193 sleep(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL);
194 system.refresh_cpu_usage();
195
196 let cpus = system.cpus();
197
198 let cpu_usage = if !cpus.is_empty() {
199 let usage = cpus.iter().map(|cpu| cpu.cpu_usage()).sum::<f32>() / cpus.len() as f32;
200 Some(usage.round() as u8)
201 } else {
202 None
203 };
204
205 let cpu_cores = cpus.len();
206
207 let cpu_model =
208 if !cpus.is_empty() { cpus[0].brand().to_string() } else { "Unknown".to_string() };
209
210 let cpu_frequency = if !cpus.is_empty() { Some(cpus[0].frequency() as f32) } else { None };
211
212 CpuInfo { cpu_usage, cpu_frequency, cpu_cores, cpu_model }
213 }
214
215 #[cfg(feature = "memory")]
223 #[inline]
224 pub fn memory() -> MemoryInfo {
225 use sysinfo::System;
226 let mut system = System::new();
227 system.refresh_memory();
228
229 let total_memory = system.total_memory() / 1024 / 1024;
230 let used_memory = system.used_memory() / 1024 / 1024;
231 let free_memory = total_memory - used_memory;
232
233 let swap_memory_total = system.total_swap() / 1024 / 1024;
234 let swap_memory_used = system.used_swap() / 1024 / 1024;
235 let swap_memory_free = swap_memory_total - swap_memory_used;
236
237 let total_memory_f32 = format_float(total_memory as f64, 2);
238 let used_memory_f32 = format_float(used_memory as f64, 2);
239 let free_memory_f32 = format_float(free_memory as f64, 2);
240
241 let swap_memory_usage_f32 = format_float(swap_memory_used as f64, 2);
242 let swap_memory_free_f32 = format_float(swap_memory_free as f64, 2);
243 let swap_memory_total_f32 = format_float(swap_memory_total as f64, 2);
244
245 let memory_usage = if total_memory > 0 {
246 Some(((used_memory as f32 / total_memory as f32) * 100.0) as u8)
247 } else {
248 None
249 };
250 let swap_memory_usage = if swap_memory_total > 0 {
251 Some(((swap_memory_used as f32 / swap_memory_total as f32) * 100.0) as u8)
252 } else {
253 None
254 };
255
256 MemoryInfo {
257 total_memory: total_memory_f32 as f32,
258 used_memory: used_memory_f32 as f32,
259 free_memory: free_memory_f32 as f32,
260 memory_usage,
261 swap_memory_total: swap_memory_total_f32 as f32,
262 swap_memory_used: swap_memory_usage_f32 as f32,
263 swap_memory_free: swap_memory_free_f32 as f32,
264 swap_memory_usage,
265 }
266 }
267
268 #[cfg(feature = "disk")]
276 #[inline]
277 pub fn disk() -> DiskInfo {
278 use sysinfo::Disks;
279 let disks = Disks::new_with_refreshed_list();
280
281 let mut total_disk_space = 0f32;
282 let mut total_used_space = 0f32;
283 let mut total_free_space = 0f32;
284 let mut disk_details = Vec::new();
285
286 for disk in disks.list() {
287 let total_space = disk.total_space() as f32 / (1024.0 * 1024.0 * 1024.0);
288 let free_space = disk.available_space() as f32 / (1024.0 * 1024.0 * 1024.0);
289 let used_space = total_space - free_space;
290
291 let usage =
292 if disk.total_space() > 0 { (used_space / total_space) * 100.0 } else { 0.0 };
293
294 let disk_detail = DiskDetail {
295 name: disk.name().to_string_lossy().to_string(),
296 total_space: format_float(total_space as f64, 2) as f32,
297 used_space: format_float(used_space as f64, 2) as f32,
298 free_space: format_float(free_space as f64, 2) as f32,
299 usage: format_float(usage as f64, 2) as f32,
300 };
301
302 total_disk_space += total_space;
303 total_used_space += used_space;
304 total_free_space += free_space;
305 disk_details.push(disk_detail);
306 }
307
308 let total_usage = if total_disk_space > 0.0 {
309 (total_used_space / total_disk_space) * 100.0
310 } else {
311 0.0
312 };
313
314 DiskInfo {
315 total_disk_space: format_float(total_disk_space as f64, 2) as f32,
316 total_used_space: format_float(total_used_space as f64, 2) as f32,
317 total_free_space: format_float(total_free_space as f64, 2) as f32,
318 total_usage: format_float(total_usage as f64, 2) as f32,
319 disks: disk_details,
320 }
321 }
322
323 #[cfg(feature = "network")]
331 pub fn network() -> Vec<NetworkInfo> {
332 let networks = Networks::new_with_refreshed_list();
333 let mut network_infos = Vec::new();
334 for (network, data) in networks.list() {
335 let mut ip_info_list: Vec<IpInfo> = Vec::new();
336
337 for ip_network in data.ip_networks() {
338 ip_info_list
339 .push(IpInfo { ip_address: ip_network.addr, netmask: ip_network.prefix });
340 }
341 network_infos.push(NetworkInfo {
342 name: network.to_string(),
343 mac_addr: data.mac_address(),
344 upload: format_float((data.total_received() as f32 / 1024.0) as f64, 2),
345 download: format_float((data.total_transmitted() as f32 / 1024.0) as f64, 2),
346 ip_info: ip_info_list,
347 });
348 }
349 network_infos
350 }
351
352 #[cfg(feature = "network")]
360 pub fn current_network() -> NetworkInfo {
361 use std::thread::sleep;
362 use std::time::Duration;
363 let mut networks = Networks::new_with_refreshed_list();
364
365 sleep(Duration::from_millis(100));
366
367 networks.refresh(true);
368
369 let process_network_data = |_: &str, data: &sysinfo::NetworkData| -> (Vec<IpInfo>, bool) {
370 let mut ip_info_list: Vec<IpInfo> = Vec::new();
371 let mut has_ipv4 = false;
372
373 for ip_network in data.ip_networks() {
374 ip_info_list
375 .push(IpInfo { ip_address: ip_network.addr, netmask: ip_network.prefix });
376
377 if ip_network.addr.is_ipv4() {
378 has_ipv4 = true;
379 }
380 }
381
382 (ip_info_list, has_ipv4)
383 };
384 let is_loopback = |name: &str| -> bool {
385 name.starts_with("lo") || name.starts_with("Loopback") || name.contains("loopback")
386 };
387
388 for (network_name, data) in networks.list() {
389 let (ip_info_list, has_ipv4) = process_network_data(network_name, data);
390
391 let recent_traffic = data.received() + data.transmitted();
392 if !is_loopback(network_name)
393 && has_ipv4 && !ip_info_list.is_empty()
394 && recent_traffic > 0
395 {
396 return NetworkInfo {
397 name: network_name.to_string(),
398 mac_addr: data.mac_address(),
399 upload: format_float(data.received() as f64 / 1024.0, 2),
400 download: format_float(data.transmitted() as f64 / 1024.0, 2),
401 ip_info: ip_info_list,
402 };
403 }
404 }
405
406 for (network_name, data) in networks.list() {
407 let (ip_info_list, has_ipv4) = process_network_data(network_name, data);
408
409 if !is_loopback(network_name) && has_ipv4 && !ip_info_list.is_empty() {
410 return NetworkInfo {
411 name: network_name.to_string(),
412 mac_addr: data.mac_address(),
413 upload: 0.0,
414 download: 0.0,
415 ip_info: ip_info_list,
416 };
417 }
418 }
419
420 NetworkInfo {
421 name: "unknown".to_string(),
422 mac_addr: MacAddr([0, 0, 0, 0, 0, 0]),
423 upload: 0.0,
424 download: 0.0,
425 ip_info: vec![],
426 }
427 }
428
429 #[cfg(feature = "process")]
435 pub fn process() -> ProcessInfo {
436 use std::process;
437 Self::process_with_pid(process::id())
438 }
439
440 #[cfg(feature = "process")]
441 pub fn process_with_pid(pid: u32) -> ProcessInfo {
442 use std::time::{SystemTime, UNIX_EPOCH};
443 use sysinfo::{ProcessesToUpdate, System};
444 let mut system = System::new();
445 let pid = Pid::from_u32(pid);
446 system.refresh_processes(ProcessesToUpdate::Some(&[pid]), true);
447 let process = system.process(pid);
448
449 let name = if let Some(process) = process {
450 process.name().to_string_lossy().into_owned()
451 } else {
452 "Unknown".to_string()
453 };
454 let start_time = process.map(|p| p.start_time()).unwrap_or(0);
455 let run_time = process
456 .map(|p| {
457 let current_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
458 let process_start_time = p.start_time();
459 current_time.saturating_sub(process_start_time)
460 })
461 .unwrap_or(0);
462
463 let cpu_usage = process.map(|p| format_float(p.cpu_usage() as f64, 2) as u8);
464
465 let memory_usage = process.map(|p| {
466 format_float(p.memory() as f64 / (system.total_memory() as f64) * 100.0, 2) as u8
467 });
468
469 let used_memory = match process {
470 Some(process) => process.memory() as f32 / 1024.0 / 1024.0,
471 None => 0.0,
472 };
473
474 ProcessInfo { pid, name, start_time, run_time, cpu_usage, memory_usage, used_memory }
475 }
476
477 #[cfg(feature = "gpu")]
485 pub fn gpu() -> Option<GpuInfo> {
486 let gpu = active_gpu();
487 match gpu {
488 Ok(gpu) => {
489 let info = gpu.info();
490 let gpu_usage = format_float(info.used_vram() as f64 / (1024.0 * 1024.0), 2) as f32;
491 let gpu_total =
492 format_float(info.total_vram() as f64 / (1024.0 * 1024.0), 2) as f32;
493 Some(GpuInfo {
494 gpu_model: gpu.model().to_string(),
495 gpu_memory_used: gpu_usage,
496 gpu_memory_total: gpu_total as f32,
497 gpu_memory_free: gpu_total - gpu_usage,
498 gpu_usage: info.load_pct() as u8,
499 })
500 }
501 Err(_) => None,
502 }
503 }
504}
505
506fn format_float(value: f64, decimals: u32) -> f64 {
507 let decimal_value = Decimal::from_f64(value).unwrap_or(Decimal::ZERO);
508 let rounded = decimal_value.round_dp(decimals);
509 rounded.to_f64().unwrap_or(0.0)
510}