diff --git a/src/deprecated/cpu_brand.rs b/src/deprecated/cpu_brand.rs new file mode 100644 index 0000000..1af76a2 --- /dev/null +++ b/src/deprecated/cpu_brand.rs @@ -0,0 +1,99 @@ +use std::fs::File; +use std::io::{self, BufRead}; + +use crate::deprecated::helper::run_command; + +/// Function to get processor information. +/// +/// # Arguments +/// +/// * `lib_path` - The path to the library used to get processor information. +/// +/// # Returns +/// +/// A `Option` containing the processor information if successful, otherwise `None`. +fn get_processor_info_darwin(lib_path: &str) -> Result { + let result = run_command(lib_path, &["-n", "machdep.cpu.brand_string"]); + if result.is_err() { + return Err("Failed to get processor info"); + } + Ok(result.unwrap()) +} + +/// Function to get processor information on Linux. +/// +/// # Arguments +/// +/// * `lib_path` - The path to the library used to get processor information. +/// +/// # Returns +/// +/// A `Option` containing the processor information if successful, otherwise `None`. +fn get_processor_info_linux(lib_path: &str) -> Result { + let file = match File::open(lib_path) { + Ok(file) => file, + Err(_) => return Err("Failed to open file"), + }; + for line in io::BufReader::new(file).lines() { + match line { + Ok(line) => { + if line.contains("model name") { + let parts: Vec<&str> = line.split(':').collect(); + if parts.len() == 2 { + return Ok(parts[1].trim().to_string()); + } + } + } + Err(_) => return Err("Error reading line"), + } + } + Err("Model name not found") +} + +/// Function to get processor information on Windows. +/// +/// # Arguments +/// +/// * `lib_path` - The path to the library used to get processor information. +/// +/// # Returns +/// +/// A `Option` containing the processor information if successful, otherwise `None`. +fn get_processor_info_windows(lib_path: &str) -> Result { + let result = run_command(lib_path, &["cpu", "get", "name"]); + let output = match result { + Ok(output) => output, + Err(_) => return Err("Failed to get processor info"), + }; + let lines: Vec<&str> = output.trim().split('\n').collect(); + if lines.len() > 1 { + Ok(lines[1].trim().to_string()) + } else { + Err("Invalid output from command") + } +} + +/// OS-agnostic function to get processor name. +/// +/// # Returns +/// +/// A `Option` containing the processor name if successful, otherwise `None`. +pub fn get_name() -> Option { + let operating_system = std::env::consts::OS; + let result = match operating_system { + "darwin" => get_processor_info_darwin("/usr/sbin/sysctl"), + "linux" => get_processor_info_linux("/proc/cpuinfo"), + "windows" => get_processor_info_windows("C:\\Windows\\System32\\wbem\\wmic.exe"), + _ => { + log::error!("Unsupported operating system: {}", operating_system); + Err("Unsupported operating system") + } + }; + match result { + Ok(info) => Some(info), + Err(err) => { + log::error!("{}", err); + None + } + } +} diff --git a/src/deprecated/disks.rs b/src/deprecated/disks.rs new file mode 100644 index 0000000..2616846 --- /dev/null +++ b/src/deprecated/disks.rs @@ -0,0 +1,224 @@ +use std::collections::HashMap; +use std::str; +use regex::Regex; +use serde_json::Value; + +use crate::deprecated::helper::{run_command, size_converter}; + +/// Function to parse size string for Linux. +/// +/// # Arguments +/// +/// * `size_str` - The size string to parse +/// +/// # Returns +/// +/// A `String` containing the parsed size string. +fn parse_size(size_str: &str) -> String { + let re = Regex::new(r"([\d.]+)([KMGTP]?)").unwrap(); + if let Some(caps) = re.captures(size_str.trim()) { + let value: f64 = caps[1].parse().unwrap(); + let unit = &caps[2]; + let unit_multipliers = HashMap::from([ + ("K", 2_f64.powi(10)), + ("M", 2_f64.powi(20)), + ("G", 2_f64.powi(30)), + ("T", 2_f64.powi(40)), + ("P", 2_f64.powi(50)), + ]); + let multiplier = unit_multipliers.get(unit).unwrap_or(&1.0); + return size_converter((value * multiplier) as u64); + } + size_str.replace("K", " KB") + .replace("M", " MB") + .replace("G", " GB") + .replace("T", " TB") + .replace("P", " PB") +} + +/// Function to check if a disk is physical/virtual for macOS. +/// +/// # Arguments +/// +/// * `lib_path` - The path to the library +/// * `device_id` - The device ID +/// +/// # Returns +/// +/// A `bool` indicating if the disk is physical. +fn is_physical_disk(lib_path: &str, device_id: &str) -> bool { + let result = run_command(lib_path, &["info", device_id]); + let output = match result { + Ok(output) => output, + Err(_) => { + log::error!("Failed to get disk info"); + return false; + }, + }; + for line in output.split("\n") { + if line.contains("Virtual:") && line.contains("Yes") { + return false; + } + } + true +} + +/// Function to get disk information on Linux. +/// +/// # Arguments +/// +/// * `lib_path` - The path to the library used to get disks' information. +/// +/// # Returns +/// +/// A `Vec` of `HashMap` containing the disk information. +fn linux_disks(lib_path: &str) -> Vec> { + let result = run_command(lib_path, &["-o", "NAME,SIZE,TYPE,MODEL", "-d"]); + let output = match result { + Ok(output) => output, + Err(_) => { + log::error!("Failed to get disk info"); + return Vec::new(); + }, + }; + let disks: Vec<&str> = output.lines().collect(); + let filtered_disks: Vec<&str> = disks.into_iter().filter(|&disk| !disk.contains("loop")).collect(); + let keys_raw = filtered_disks[0].to_lowercase() + .replace("name", "DeviceID") + .replace("model", "Name") + .replace("size", "Size"); + let keys: Vec<&str> = keys_raw.split_whitespace().collect(); + let mut disk_info = Vec::new(); + for line in &filtered_disks[1..] { + let values: Vec<&str> = line.split_whitespace().collect(); + let mut disk_map = HashMap::new(); + for (key, value) in keys.iter().zip(values.iter()) { + disk_map.insert(key.to_string(), value.to_string()); + } + disk_map.insert("Size".to_string(), parse_size(disk_map.get("Size").unwrap())); + disk_map.remove("type"); + disk_info.push(disk_map); + } + disk_info +} + +/// Function to get disk information on macOS. +/// +/// # Arguments +/// +/// * `lib_path` - The path to the library used to get disks' information. +/// +/// # Returns +/// +/// A `Vec` of `HashMap` containing the disk information. +fn darwin_disks(lib_path: &str) -> Vec> { + let result = run_command(lib_path, &["list"]); + let output = match result { + Ok(output) => output, + Err(_) => { + log::error!("Failed to get disk info"); + return Vec::new(); + }, + }; + let disks: Vec<&str> = output.lines().collect(); + let disk_lines: Vec<&str> = disks.into_iter().filter(|&line| line.starts_with("/dev/disk")).collect(); + let mut disk_info = Vec::new(); + for line in disk_lines { + let device_id = line.split_whitespace().next().unwrap(); + if !is_physical_disk(lib_path, device_id) { + continue; + } + let result = run_command(lib_path, &["info", device_id]); + let disk_info_output = match result { + Ok(output) => output, + Err(_) => { + log::error!("Failed to get disk info"); + return Vec::new(); + }, + }; + let info_lines: Vec<&str> = disk_info_output.lines().collect(); + let mut disk_data = HashMap::new(); + for info_line in info_lines { + if info_line.contains("Device / Media Name:") { + disk_data.insert("Name".to_string(), info_line.split(":").nth(1).unwrap().trim().to_string()); + } + if info_line.contains("Disk Size:") { + let size_info = info_line.split(":").nth(1).unwrap().split("(").next().unwrap().trim().to_string(); + disk_data.insert("Size".to_string(), size_info); + } + } + disk_data.insert("DeviceID".to_string(), device_id.to_string()); + disk_info.push(disk_data); + } + disk_info +} + +/// Function to reformat disk information on Windows. +/// +/// # Arguments +/// +/// * `data` - A mutable reference to the disk information. +/// +/// # Returns +/// +/// A `HashMap` containing the reformatted disk information. +fn reformat_windows(data: &mut HashMap) -> HashMap { + let size = data.get("Size").unwrap().as_f64().unwrap(); + let model = data.get("Model").unwrap().as_str().unwrap().to_string(); + let mut reformatted_data = HashMap::new(); + reformatted_data.insert("Size".to_string(), size_converter(size as u64)); + reformatted_data.insert("Name".to_string(), model); + reformatted_data.insert("DeviceID".to_string(), data.get("DeviceID").unwrap().as_str().unwrap().to_string()); + reformatted_data +} + +/// Function to get disk information on Windows. +/// +/// # Arguments +/// +/// * `lib_path` - The path to the library used to get disks' information. +/// +/// # Returns +/// +/// A `Vec` of `HashMap` containing the disk information. +fn windows_disks(lib_path: &str) -> Vec> { + let ps_command = "Get-CimInstance Win32_DiskDrive | Select-Object Caption, DeviceID, Model, Partitions, Size | ConvertTo-Json"; + let result = run_command(lib_path, &["-Command", ps_command]); + let output = match result { + Ok(output) => output, + Err(_) => { + log::error!("Failed to get disk info"); + return Vec::new(); + }, + }; + let disks_info: Value = serde_json::from_str(&output).unwrap(); + let mut disk_info = Vec::new(); + if let Some(disks) = disks_info.as_array() { + for disk in disks { + let mut disk_map: HashMap = serde_json::from_value(disk.clone()).unwrap(); + disk_info.push(reformat_windows(&mut disk_map)); + } + } else { + let mut disk_map: HashMap = serde_json::from_value(disks_info).unwrap(); + disk_info.push(reformat_windows(&mut disk_map)); + } + disk_info +} + +/// Function to get all disks' information. +/// +/// # Returns +/// +/// A `Vec` of `HashMap` containing the disk information. +pub fn get_all_disks() -> Vec> { + let operating_system = std::env::consts::OS; + match operating_system { + "windows" => windows_disks("C:\\Program Files\\PowerShell\\7\\pwsh.exe"), + "macos" => darwin_disks("/usr/sbin/diskutil"), + "linux" => linux_disks("/usr/bin/lsblk"), + _ => { + log::error!("Unsupported operating system"); + Vec::new() + } + } +} diff --git a/src/deprecated/helper.rs b/src/deprecated/helper.rs new file mode 100644 index 0000000..758613e --- /dev/null +++ b/src/deprecated/helper.rs @@ -0,0 +1,58 @@ +use std::process::Command; + +/// Function to convert byte size to human-readable format +/// +/// # Arguments +/// +/// * `byte_size` - The size in bytes to convert +/// +/// # Returns +/// +/// A `String` containing the human-readable format of the byte size +pub fn size_converter(byte_size: u64) -> String { + let size_name = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; + let mut index = 0; + let mut size = byte_size as f64; + + while size >= 1024.0 && index < size_name.len() - 1 { + size /= 1024.0; + index += 1; + } + + format!("{:.2} {}", size, size_name[index]) +} + + +/// Function to parse size string. +/// +/// # Arguments +/// +/// * `size_str` - The size string to parse +/// * `unit` - The unit to convert the size to +/// +/// # Returns +/// +/// A `String` containing the parsed size string. +pub fn run_command(command: &str, args: &[&str]) -> Result { + match Command::new(command) + .args(args) + .output() + { + Ok(output) => { + if output.status.success() { + log::debug!("Command [{}] executed successfully", &command); + Ok(String::from_utf8_lossy(&output.stdout).trim().to_string()) + } else { + let stderr = String::from_utf8_lossy(&output.stderr).to_string(); + let exit_code = output.status.code().unwrap_or(-1); + log::error!("Command [{}] failed with exit code: {}", command, exit_code); + log::error!("Stderr: {}", stderr); + Err(stderr) + } + } + Err(err) => { + log::error!("Failed to execute command: {}", err); + Err(err.to_string()) + } + } +} diff --git a/src/deprecated/mod.rs b/src/deprecated/mod.rs new file mode 100644 index 0000000..b47eb6a --- /dev/null +++ b/src/deprecated/mod.rs @@ -0,0 +1,17 @@ +use std::{collections::HashMap, process::Command}; + +/// This module contains functions that gathers CPU brand information. +pub mod cpu_brand; +/// This module contains disk related functions. +pub mod disks; + +pub mod helper; + +fn get_all_disks() -> Vec> { + disks::get_all_disks() +} + + +fn get_cpu_brand() -> Option { + cpu_brand::get_name() +} diff --git a/src/lib.rs b/src/lib.rs index 4f0a2c0..f818483 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,8 +14,12 @@ mod constant; mod routes; /// Module to store all the helper functions. mod squire; +/// Module to store all the HTML templates rendered using Jinja. mod templates; +/// Module for functions related to system resources. mod resources; +/// Module for deprecated (but still useful for reference) functions +mod deprecated; /// Contains entrypoint and initializer settings to trigger the asynchronous `HTTPServer` /// diff --git a/src/resources/stream.rs b/src/resources/stream.rs index a079bf9..9bd7f48 100644 --- a/src/resources/stream.rs +++ b/src/resources/stream.rs @@ -90,15 +90,12 @@ fn get_system_metrics() -> HashMap { system.refresh_all(); let load_avg = System::load_average(); - let mem_total = system.total_memory(); - - // used_memory uses "mem_total - mem_free" but memory is set to available instead of free in macOS let mut hash_vec = vec![ ( "memory_info".to_string(), serde_json::json!({ - "total": mem_total, - "used": mem_total - system.available_memory(), + "total": system.total_memory(), + "used": system.used_memory(), }), ), (