You've already forked neo64fetch
mirror of
https://github.com/neoarz/neo64fetch.git
synced 2026-02-09 06:43:26 +01:00
feat: get all info
This commit is contained in:
100
src/helpers/battery.rs
Normal file
100
src/helpers/battery.rs
Normal file
@@ -0,0 +1,100 @@
|
||||
use std::process::Command;
|
||||
|
||||
pub fn get_battery_info() -> String {
|
||||
let output = Command::new("ioreg")
|
||||
.args(["-l", "-w0", "-r", "-c", "AppleSmartBattery"])
|
||||
.output();
|
||||
|
||||
let stdout = match output {
|
||||
Ok(out) => String::from_utf8_lossy(&out.stdout).to_string(),
|
||||
Err(_) => return "<unknown>".to_string(),
|
||||
};
|
||||
|
||||
let mut device_name = "Built-in".to_string();
|
||||
let mut current_capacity: Option<i32> = None;
|
||||
let mut external_connected = false;
|
||||
let mut is_charging = false;
|
||||
let mut avg_time_to_empty: Option<i32> = None;
|
||||
|
||||
for line in stdout.lines() {
|
||||
if line.contains("\"DeviceName\"") {
|
||||
if let Some(equals_pos) = line.find('=') {
|
||||
let value_part = &line[equals_pos + 1..].trim();
|
||||
let value = value_part.trim_matches('"').trim_matches(';').trim();
|
||||
if !value.is_empty() {
|
||||
device_name = value.to_string();
|
||||
}
|
||||
}
|
||||
} else if line.contains("\"CurrentCapacity\"") {
|
||||
if let Some(equals_pos) = line.find('=') {
|
||||
let value_part = &line[equals_pos + 1..].trim();
|
||||
let value = value_part.trim_matches(';').trim();
|
||||
if let Ok(capacity) = value.parse::<i32>() {
|
||||
current_capacity = Some(capacity);
|
||||
}
|
||||
}
|
||||
} else if line.contains("\"ExternalConnected\"") {
|
||||
if let Some(equals_pos) = line.find('=') {
|
||||
let value_part = &line[equals_pos + 1..].trim();
|
||||
let value = value_part.trim_matches(';').trim();
|
||||
external_connected = value == "Yes";
|
||||
}
|
||||
} else if line.contains("\"IsCharging\"") {
|
||||
if let Some(equals_pos) = line.find('=') {
|
||||
let value_part = &line[equals_pos + 1..].trim();
|
||||
let value = value_part.trim_matches(';').trim();
|
||||
is_charging = value == "Yes";
|
||||
}
|
||||
} else if line.contains("\"AvgTimeToEmpty\"") {
|
||||
if let Some(equals_pos) = line.find('=') {
|
||||
let value_part = &line[equals_pos + 1..].trim();
|
||||
let value = value_part.trim_matches(';').trim();
|
||||
if let Ok(time) = value.parse::<i32>() {
|
||||
avg_time_to_empty = Some(time);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let percentage = if let Some(capacity) = current_capacity {
|
||||
if capacity >= 0 && capacity <= 100 {
|
||||
capacity as u32
|
||||
} else {
|
||||
return "<unknown>".to_string();
|
||||
}
|
||||
} else {
|
||||
return "<unknown>".to_string();
|
||||
};
|
||||
|
||||
let mut status = String::new();
|
||||
if external_connected {
|
||||
status.push_str("AC connected");
|
||||
} else if is_charging {
|
||||
status.push_str("Charging");
|
||||
} else {
|
||||
status.push_str("Discharging");
|
||||
}
|
||||
|
||||
let mut result = format!("Battery ({}): {}%", device_name, percentage);
|
||||
|
||||
if !external_connected && !is_charging {
|
||||
if let Some(time_mins) = avg_time_to_empty {
|
||||
if time_mins > 0 && time_mins < 0xFFFF {
|
||||
let hours = time_mins / 60;
|
||||
let mins = time_mins % 60;
|
||||
|
||||
if hours > 0 && mins > 0 {
|
||||
result.push_str(&format!(" ({} hours, {} mins remaining)", hours, mins));
|
||||
} else if hours > 0 {
|
||||
result.push_str(&format!(" ({} hours remaining)", hours));
|
||||
} else if mins > 0 {
|
||||
result.push_str(&format!(" ({} mins remaining)", mins));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.push_str(&format!(" [{}]", status));
|
||||
|
||||
result
|
||||
}
|
||||
63
src/helpers/cpu.rs
Normal file
63
src/helpers/cpu.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
// https://github.com/fastfetch-cli/fastfetch/blob/dev/src/detection/cpu/cpu_apple.c
|
||||
|
||||
use std::process::Command;
|
||||
|
||||
pub fn get_cpu_info() -> String {
|
||||
let brand = Command::new("sysctl")
|
||||
.args(["-n", "machdep.cpu.brand_string"])
|
||||
.output()
|
||||
.map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
|
||||
.unwrap_or_else(|_| "Unknown CPU".to_string());
|
||||
|
||||
let cores = Command::new("sysctl")
|
||||
.args(["-n", "hw.physicalcpu"])
|
||||
.output()
|
||||
.map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
|
||||
.unwrap_or_else(|_| "0".to_string());
|
||||
|
||||
let ioreg_output = Command::new("ioreg")
|
||||
.args(["-p", "IODeviceTree", "-n", "pmgr", "-l"])
|
||||
.output();
|
||||
|
||||
let mut max_freq = 0u64;
|
||||
|
||||
if let Ok(output) = ioreg_output {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
for line in stdout.lines() {
|
||||
if line.contains("voltage-states5-sram") {
|
||||
let hex_data = line
|
||||
.split('<')
|
||||
.nth(1)
|
||||
.and_then(|s| s.split('>').next())
|
||||
.unwrap_or("");
|
||||
for i in (0..hex_data.len()).step_by(16) {
|
||||
if i + 8 <= hex_data.len() {
|
||||
let chunk = &hex_data[i..i + 8];
|
||||
let mut bytes = [0u8; 4];
|
||||
for j in 0..4 {
|
||||
if let Ok(byte) = u8::from_str_radix(&chunk[j * 2..j * 2 + 2], 16) {
|
||||
bytes[j] = byte;
|
||||
}
|
||||
}
|
||||
let freq = u32::from_le_bytes(bytes) as u64;
|
||||
if freq > max_freq {
|
||||
max_freq = freq;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if max_freq == 0 {
|
||||
return format!("{} ({})", brand, cores);
|
||||
}
|
||||
|
||||
let ghz = if max_freq > 100_000_000 {
|
||||
max_freq as f64 / 1_000_000_000.0
|
||||
} else {
|
||||
max_freq as f64 / 1_000_000.0
|
||||
};
|
||||
|
||||
format!("{} ({}) @ {:.2} GHz", brand, cores, ghz)
|
||||
}
|
||||
@@ -4,25 +4,6 @@ use plist::Value;
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn format_color(dict: &plist::dictionary::Dictionary) -> String {
|
||||
let r = (dict.get("red").and_then(|v| v.as_real()).unwrap_or(0.0) * 255.0 + 0.5) as u32;
|
||||
let g = (dict.get("green").and_then(|v| v.as_real()).unwrap_or(0.0) * 255.0 + 0.5) as u32;
|
||||
let b = (dict.get("blue").and_then(|v| v.as_real()).unwrap_or(0.0) * 255.0 + 0.5) as u32;
|
||||
let a = (dict.get("alpha").and_then(|v| v.as_real()).unwrap_or(1.0) * 255.0 + 0.5) as u32;
|
||||
|
||||
let color_hex = (r << 24) | (g << 16) | (b << 8) | a;
|
||||
|
||||
match color_hex {
|
||||
0x000000FF => "Black".to_string(),
|
||||
0xFFFFFFFF => "White".to_string(),
|
||||
0xFF2600FF => "Red".to_string(),
|
||||
0x0433FFFF => "Blue".to_string(),
|
||||
0x00F900FF => "Green".to_string(),
|
||||
0xFFFB00FF => "Yellow".to_string(),
|
||||
_ => format!("#{:08X}", color_hex),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_cursor_info() -> String {
|
||||
let mut path = PathBuf::from(env::var("HOME").unwrap_or_default());
|
||||
path.push("Library/Preferences/com.apple.universalaccess.plist");
|
||||
@@ -34,10 +15,37 @@ pub fn get_cursor_info() -> String {
|
||||
if let Ok(value) = Value::from_file(path) {
|
||||
if let Some(dict) = value.as_dictionary() {
|
||||
if let Some(f_dict) = dict.get("cursorFill").and_then(|v| v.as_dictionary()) {
|
||||
fill = format_color(f_dict);
|
||||
let r = (f_dict.get("red").and_then(|v| v.as_real()).unwrap_or(0.0) * 255.0 + 0.5) as u32;
|
||||
let g = (f_dict.get("green").and_then(|v| v.as_real()).unwrap_or(0.0) * 255.0 + 0.5) as u32;
|
||||
let b = (f_dict.get("blue").and_then(|v| v.as_real()).unwrap_or(0.0) * 255.0 + 0.5) as u32;
|
||||
let a = (f_dict.get("alpha").and_then(|v| v.as_real()).unwrap_or(1.0) * 255.0 + 0.5) as u32;
|
||||
let color_hex = (r << 24) | (g << 16) | (b << 8) | a;
|
||||
fill = match color_hex {
|
||||
0x000000FF => "Black".to_string(),
|
||||
0xFFFFFFFF => "White".to_string(),
|
||||
0xFF2600FF => "Red".to_string(),
|
||||
0x0433FFFF => "Blue".to_string(),
|
||||
0x00F900FF => "Green".to_string(),
|
||||
0xFFFB00FF => "Yellow".to_string(),
|
||||
_ => format!("#{:08X}", color_hex),
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(o_dict) = dict.get("cursorOutline").and_then(|v| v.as_dictionary()) {
|
||||
outline = format_color(o_dict);
|
||||
let r = (o_dict.get("red").and_then(|v| v.as_real()).unwrap_or(0.0) * 255.0 + 0.5) as u32;
|
||||
let g = (o_dict.get("green").and_then(|v| v.as_real()).unwrap_or(0.0) * 255.0 + 0.5) as u32;
|
||||
let b = (o_dict.get("blue").and_then(|v| v.as_real()).unwrap_or(0.0) * 255.0 + 0.5) as u32;
|
||||
let a = (o_dict.get("alpha").and_then(|v| v.as_real()).unwrap_or(1.0) * 255.0 + 0.5) as u32;
|
||||
let color_hex = (r << 24) | (g << 16) | (b << 8) | a;
|
||||
outline = match color_hex {
|
||||
0x000000FF => "Black".to_string(),
|
||||
0xFFFFFFFF => "White".to_string(),
|
||||
0xFF2600FF => "Red".to_string(),
|
||||
0x0433FFFF => "Blue".to_string(),
|
||||
0x00F900FF => "Green".to_string(),
|
||||
0xFFFB00FF => "Yellow".to_string(),
|
||||
_ => format!("#{:08X}", color_hex),
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(s_val) = dict.get("mouseDriverCursorSize").and_then(|v| v.as_real()) {
|
||||
|
||||
100
src/helpers/gpu.rs
Normal file
100
src/helpers/gpu.rs
Normal file
@@ -0,0 +1,100 @@
|
||||
// https://github.com/fastfetch-cli/fastfetch/blob/dev/src/detection/gpu/gpu_apple.c
|
||||
|
||||
use std::process::Command;
|
||||
|
||||
pub fn get_gpu_info() -> String {
|
||||
let ioreg_accel = Command::new("ioreg")
|
||||
.args(["-rc", "IOAccelerator", "-d", "1"])
|
||||
.output();
|
||||
|
||||
let mut model = String::new();
|
||||
let mut cores = String::new();
|
||||
let mut vendor_id = String::new();
|
||||
|
||||
if let Ok(output) = ioreg_accel {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
for line in stdout.lines() {
|
||||
if line.contains("\"model\"") {
|
||||
model = line
|
||||
.split('=')
|
||||
.nth(1)
|
||||
.unwrap_or("")
|
||||
.trim()
|
||||
.replace('"', "")
|
||||
.replace('<', "")
|
||||
.replace('>', "");
|
||||
}
|
||||
if line.contains("\"gpu-core-count\"") {
|
||||
cores = line.split('=').nth(1).unwrap_or("").trim().to_string();
|
||||
}
|
||||
if line.contains("\"vendor-id\"") {
|
||||
vendor_id = line.split('=').nth(1).unwrap_or("").trim().to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apple (0x106b) or Intel (0x8086) = Integrated
|
||||
let type_str = match vendor_id.to_lowercase().as_str() {
|
||||
"0x106b" | "4203" => "[Integrated]",
|
||||
"0x8086" | "32902" => "[Integrated]",
|
||||
"0x1002" | "4098" => "[Discrete]",
|
||||
"0x10de" | "4318" => "[Discrete]",
|
||||
_ => "[Integrated]", // Default for Apple Silicon if vendor-id is weird
|
||||
};
|
||||
|
||||
let ioreg_pmgr = Command::new("ioreg")
|
||||
.args(["-p", "IODeviceTree", "-n", "pmgr", "-l"])
|
||||
.output();
|
||||
|
||||
let mut max_freq = 0u64;
|
||||
if let Ok(output) = ioreg_pmgr {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
for line in stdout.lines() {
|
||||
if line.contains("voltage-states9-sram") {
|
||||
let hex_data = line
|
||||
.split('<')
|
||||
.nth(1)
|
||||
.and_then(|s| s.split('>').next())
|
||||
.unwrap_or("");
|
||||
for i in (0..hex_data.len()).step_by(16) {
|
||||
if i + 8 <= hex_data.len() {
|
||||
let chunk = &hex_data[i..i + 8];
|
||||
let mut bytes = [0u8; 4];
|
||||
for j in 0..4 {
|
||||
if let Ok(byte) = u8::from_str_radix(&chunk[j * 2..j * 2 + 2], 16) {
|
||||
bytes[j] = byte;
|
||||
}
|
||||
}
|
||||
let freq = u32::from_le_bytes(bytes) as u64;
|
||||
if freq > max_freq {
|
||||
max_freq = freq;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let freq_str = if max_freq > 0 {
|
||||
let ghz = if max_freq > 100_000_000 {
|
||||
max_freq as f64 / 1_000_000_000.0
|
||||
} else {
|
||||
max_freq as f64 / 1_000_000.0
|
||||
};
|
||||
format!(" @ {:.2} GHz", ghz)
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
|
||||
let core_str = if !cores.is_empty() {
|
||||
format!(" ({})", cores)
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
|
||||
if model.is_empty() {
|
||||
"Unknown GPU".to_string()
|
||||
} else {
|
||||
format!("{}{} {} {}", model, core_str, freq_str, type_str).replace(" ", " ")
|
||||
}
|
||||
}
|
||||
55
src/helpers/ip.rs
Normal file
55
src/helpers/ip.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
use std::process::Command;
|
||||
|
||||
pub fn get_ip_info() -> String {
|
||||
let mut interface = "en0".to_string();
|
||||
|
||||
let route_output = Command::new("route")
|
||||
.args(["get", "default"])
|
||||
.output();
|
||||
|
||||
if let Ok(output) = route_output {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
for line in stdout.lines() {
|
||||
if line.trim().starts_with("interface:") {
|
||||
if let Some(iface) = line.split_whitespace().nth(1) {
|
||||
interface = iface.to_string();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let ifconfig_output = Command::new("ifconfig")
|
||||
.arg(&interface)
|
||||
.output();
|
||||
|
||||
if let Ok(output) = ifconfig_output {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
for line in stdout.lines() {
|
||||
if line.contains("inet ") && !line.contains("127.0.0.1") {
|
||||
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||
if parts.len() >= 2 {
|
||||
let ip = parts[1];
|
||||
let mut ip_with_cidr = ip.to_string();
|
||||
|
||||
if let Some(netmask_idx) = parts.iter().position(|&x| x == "netmask") {
|
||||
if netmask_idx + 1 < parts.len() {
|
||||
let netmask_hex = parts[netmask_idx + 1];
|
||||
if netmask_hex.starts_with("0x") {
|
||||
if let Ok(num) = u32::from_str_radix(&netmask_hex[2..], 16) {
|
||||
let cidr = num.count_ones();
|
||||
ip_with_cidr = format!("{}/{}", ip, cidr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return format!("Local IP ({}): {}", interface, ip_with_cidr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"<unknown>".to_string()
|
||||
}
|
||||
|
||||
18
src/helpers/locale.rs
Normal file
18
src/helpers/locale.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use std::env;
|
||||
|
||||
pub fn get_locale_info() -> String {
|
||||
if let Ok(locale) = env::var("LC_ALL") {
|
||||
if !locale.is_empty() {
|
||||
return format!("Locale: {}", locale);
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(locale) = env::var("LANG") {
|
||||
if !locale.is_empty() {
|
||||
return format!("Locale: {}", locale);
|
||||
}
|
||||
}
|
||||
|
||||
"<unknown>".to_string()
|
||||
}
|
||||
|
||||
79
src/helpers/memory.rs
Normal file
79
src/helpers/memory.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
// https://github.com/fastfetch-cli/fastfetch/blob/dev/src/detection/memory/memory_apple.c
|
||||
|
||||
use libc::{
|
||||
HOST_VM_INFO64, HOST_VM_INFO64_COUNT, host_statistics64, mach_host_self, vm_statistics64_data_t,
|
||||
};
|
||||
use std::mem;
|
||||
use std::process::Command;
|
||||
|
||||
pub fn get_memory_info() -> String {
|
||||
let total_bytes = Command::new("sysctl")
|
||||
.args(["-n", "hw.memsize"])
|
||||
.output()
|
||||
.map(|o| {
|
||||
String::from_utf8_lossy(&o.stdout)
|
||||
.trim()
|
||||
.parse::<u64>()
|
||||
.unwrap_or(0)
|
||||
})
|
||||
.unwrap_or(0);
|
||||
|
||||
let usable_bytes = Command::new("sysctl")
|
||||
.args(["-n", "hw.memsize_usable"])
|
||||
.output()
|
||||
.map(|o| {
|
||||
String::from_utf8_lossy(&o.stdout)
|
||||
.trim()
|
||||
.parse::<u64>()
|
||||
.unwrap_or(total_bytes)
|
||||
})
|
||||
.unwrap_or(total_bytes);
|
||||
|
||||
let mut used_bytes: u64 = 0;
|
||||
|
||||
// mach_host_self, and HOST_VM_INFO64 are macos c functions (ffi), so we HAVE to use unsafe
|
||||
// i needa learn a better way to do this tbh
|
||||
// learned smth new lol
|
||||
unsafe {
|
||||
let mut count = HOST_VM_INFO64_COUNT;
|
||||
let mut vmstat: vm_statistics64_data_t = mem::zeroed(); // this this is unsafe we have to manually zero it
|
||||
|
||||
if host_statistics64(
|
||||
mach_host_self(),
|
||||
HOST_VM_INFO64,
|
||||
&mut vmstat as *mut _ as *mut _,
|
||||
&mut count,
|
||||
) == 0
|
||||
{
|
||||
let page_size = Command::new("pagesize")
|
||||
.output()
|
||||
.map(|o| {
|
||||
String::from_utf8_lossy(&o.stdout)
|
||||
.trim()
|
||||
.parse::<u64>()
|
||||
.unwrap_or(4096)
|
||||
})
|
||||
.unwrap_or(4096);
|
||||
|
||||
let app_memory = (vmstat.internal_page_count as u64) * page_size;
|
||||
let wired_memory = (vmstat.wire_count as u64) * page_size;
|
||||
let compressed_memory = (vmstat.compressor_page_count as u64) * page_size;
|
||||
let reserved_memory = total_bytes.saturating_sub(usable_bytes);
|
||||
|
||||
used_bytes = app_memory + wired_memory + compressed_memory + reserved_memory;
|
||||
}
|
||||
}
|
||||
|
||||
let total_gib = total_bytes as f64 / 1073741824.0;
|
||||
let used_gib = used_bytes as f64 / 1073741824.0;
|
||||
let percentage = if total_bytes > 0 {
|
||||
(used_bytes as f64 / total_bytes as f64) * 100.0
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
format!(
|
||||
"{:.2} GiB / {:.2} GiB ({:.0}%)",
|
||||
used_gib, total_gib, percentage
|
||||
)
|
||||
}
|
||||
@@ -1,9 +1,17 @@
|
||||
pub mod battery;
|
||||
pub mod cpu;
|
||||
pub mod cursor;
|
||||
pub mod desktop_env;
|
||||
pub mod display;
|
||||
pub mod font;
|
||||
pub mod gpu;
|
||||
pub mod ip;
|
||||
pub mod locale;
|
||||
pub mod memory;
|
||||
pub mod packages;
|
||||
pub mod shell;
|
||||
pub mod storage;
|
||||
pub mod swap;
|
||||
pub mod terminal;
|
||||
pub mod terminal_font;
|
||||
pub mod uptime;
|
||||
|
||||
100
src/helpers/storage.rs
Normal file
100
src/helpers/storage.rs
Normal file
@@ -0,0 +1,100 @@
|
||||
// https://github.com/fastfetch-cli/fastfetch/blob/dev/src/detection/disk/disk.c
|
||||
|
||||
use libc::{c_int, c_char};
|
||||
use std::ffi::CStr;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
struct Statfs {
|
||||
f_bsize: u32,
|
||||
f_iosize: c_int,
|
||||
f_blocks: u64,
|
||||
f_bfree: u64,
|
||||
f_bavail: u64,
|
||||
f_files: u64,
|
||||
f_ffree: u64,
|
||||
f_fsid: [u32; 2],
|
||||
f_owner: u32,
|
||||
f_type: u32,
|
||||
f_flags: u32,
|
||||
f_fssubtype: u32,
|
||||
f_fstypename: [c_char; 16],
|
||||
f_mntonname: [c_char; 1024],
|
||||
f_mntfromname: [c_char; 1024],
|
||||
f_reserved: [u32; 8],
|
||||
}
|
||||
|
||||
unsafe extern "C" {
|
||||
fn getfsstat(buf: *mut Statfs, bufsize: c_int, flags: c_int) -> c_int;
|
||||
}
|
||||
|
||||
const MNT_WAIT: c_int = 1;
|
||||
const MNT_NOWAIT: c_int = 2;
|
||||
const MNT_RDONLY: u32 = 0x00000001;
|
||||
|
||||
pub fn get_storage_info() -> String {
|
||||
unsafe {
|
||||
let size = getfsstat(std::ptr::null_mut(), 0, MNT_WAIT);
|
||||
if size <= 0 {
|
||||
return "<unknown>".to_string();
|
||||
}
|
||||
|
||||
let statfs_size = std::mem::size_of::<Statfs>();
|
||||
if statfs_size != 2168 {
|
||||
return "<unknown>".to_string();
|
||||
}
|
||||
|
||||
let mut buf = vec![std::mem::zeroed::<Statfs>(); size as usize];
|
||||
let bufsize = (statfs_size * size as usize) as c_int;
|
||||
|
||||
let result = getfsstat(buf.as_mut_ptr(), bufsize, MNT_NOWAIT);
|
||||
if result <= 0 {
|
||||
return "<unknown>".to_string();
|
||||
}
|
||||
|
||||
for fs in &buf {
|
||||
let mountpoint = CStr::from_ptr(fs.f_mntonname.as_ptr() as *const c_char)
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
if mountpoint == "/" {
|
||||
let filesystem = CStr::from_ptr(fs.f_fstypename.as_ptr() as *const c_char)
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
let block_size = fs.f_bsize as u64;
|
||||
let total_bytes = fs.f_blocks * block_size;
|
||||
let available_bytes = fs.f_bavail * block_size;
|
||||
let used_bytes = total_bytes.saturating_sub(available_bytes);
|
||||
|
||||
let total_gib = total_bytes as f64 / 1073741824.0;
|
||||
let used_gib = used_bytes as f64 / 1073741824.0;
|
||||
let percentage = if total_bytes > 0 {
|
||||
(used_bytes as f64 / total_bytes as f64) * 100.0
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
let read_only = (fs.f_flags & MNT_RDONLY) != 0;
|
||||
|
||||
let mut result = format!(
|
||||
"{:.2} GiB / {:.2} GiB ({:.0}%)",
|
||||
used_gib, total_gib, percentage
|
||||
);
|
||||
|
||||
if !filesystem.is_empty() {
|
||||
result.push_str(&format!(" - {}", filesystem));
|
||||
}
|
||||
|
||||
if read_only {
|
||||
result.push_str(" [Read-only]");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"<unknown>".to_string()
|
||||
}
|
||||
|
||||
41
src/helpers/swap.rs
Normal file
41
src/helpers/swap.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
// https://github.com/fastfetch-cli/fastfetch/blob/dev/src/detection/swap/swap_apple.c
|
||||
|
||||
use std::process::Command;
|
||||
|
||||
pub fn get_swap_info() -> String {
|
||||
let output = Command::new("sysctl").args(["-n", "vm.swapusage"]).output();
|
||||
|
||||
if let Ok(out) = output {
|
||||
let stdout = String::from_utf8_lossy(&out.stdout);
|
||||
|
||||
let mut total_mb = 0.0;
|
||||
let mut used_mb = 0.0;
|
||||
|
||||
for part in stdout.split_whitespace() {
|
||||
if let Some(val) = part.strip_suffix('M') {
|
||||
if let Ok(num) = val.parse::<f64>() {
|
||||
if stdout.contains(&format!("total = {}", part)) {
|
||||
total_mb = num;
|
||||
} else if stdout.contains(&format!("used = {}", part)) {
|
||||
used_mb = num;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let total_gib = total_mb / 1024.0;
|
||||
let used_gib = used_mb / 1024.0;
|
||||
let percentage = if total_mb > 0.0 {
|
||||
(used_mb / total_mb) * 100.0
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
return format!(
|
||||
"{:.2} GiB / {:.2} GiB ({:.0}%)",
|
||||
used_gib, total_gib, percentage
|
||||
);
|
||||
}
|
||||
|
||||
"0.00 GiB / 0.00 GiB (0%)".to_string()
|
||||
}
|
||||
24
src/main.rs
24
src/main.rs
@@ -25,6 +25,14 @@ struct Stats {
|
||||
cursor: String,
|
||||
terminal: String,
|
||||
terminal_font: String,
|
||||
cpu: String,
|
||||
gpu: String,
|
||||
memory: String,
|
||||
swap: String,
|
||||
storage: String,
|
||||
ip: String,
|
||||
battery: String,
|
||||
locale: String,
|
||||
|
||||
// Extra fields
|
||||
architecture: String,
|
||||
@@ -54,6 +62,14 @@ fn main() {
|
||||
cursor: helpers::cursor::get_cursor_info(),
|
||||
terminal: helpers::terminal::get_terminal_info(),
|
||||
terminal_font: helpers::terminal_font::get_terminal_font_info(),
|
||||
cpu: helpers::cpu::get_cpu_info(),
|
||||
gpu: helpers::gpu::get_gpu_info(),
|
||||
memory: helpers::memory::get_memory_info(),
|
||||
swap: helpers::swap::get_swap_info(),
|
||||
storage: helpers::storage::get_storage_info(),
|
||||
ip: helpers::ip::get_ip_info(),
|
||||
battery: helpers::battery::get_battery_info(),
|
||||
locale: helpers::locale::get_locale_info(),
|
||||
};
|
||||
|
||||
// TODO: Add ascii art support later
|
||||
@@ -76,5 +92,13 @@ fn main() {
|
||||
println!("{}", stats.cursor);
|
||||
println!("{}", stats.terminal);
|
||||
println!("{}", stats.terminal_font);
|
||||
println!("{}", stats.cpu);
|
||||
println!("{}", stats.gpu);
|
||||
println!("{}", stats.memory);
|
||||
println!("{}", stats.swap);
|
||||
println!("{}", stats.storage);
|
||||
println!("{}", stats.ip);
|
||||
println!("{}", stats.battery);
|
||||
println!("{}", stats.locale);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user