feat: halfway there?

This commit is contained in:
neoarz
2026-01-05 20:42:12 -05:00
parent 136b77644c
commit a1b7e755b2
11 changed files with 420 additions and 2 deletions

132
Cargo.lock generated
View File

@@ -2,6 +2,12 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 4
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.3.2" version = "1.3.2"
@@ -54,6 +60,15 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f"
[[package]]
name = "deranged"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587"
dependencies = [
"powerfmt",
]
[[package]] [[package]]
name = "dispatch2" name = "dispatch2"
version = "0.3.0" version = "0.3.0"
@@ -91,6 +106,12 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]] [[package]]
name = "errno" name = "errno"
version = "0.3.14" version = "0.3.14"
@@ -116,6 +137,28 @@ dependencies = [
"byteorder", "byteorder",
] ]
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
[[package]]
name = "indexmap"
version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "itoa"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.179" version = "0.2.179"
@@ -155,6 +198,10 @@ version = "0.1.0"
dependencies = [ dependencies = [
"colored", "colored",
"display-info", "display-info",
"objc2",
"objc2-app-kit",
"objc2-foundation",
"plist",
"sysinfo", "sysinfo",
] ]
@@ -167,6 +214,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]] [[package]]
name = "objc2" name = "objc2"
version = "0.6.3" version = "0.6.3"
@@ -351,6 +404,25 @@ version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "plist"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07"
dependencies = [
"base64",
"indexmap",
"quick-xml 0.38.4",
"serde",
"time",
]
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.104" version = "1.0.104"
@@ -406,6 +478,35 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "shlex" name = "shlex"
version = "1.3.0" version = "1.3.0"
@@ -488,6 +589,37 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "time"
version = "0.3.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d"
dependencies = [
"deranged",
"itoa",
"num-conv",
"powerfmt",
"serde",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b"
[[package]]
name = "time-macros"
version = "0.2.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3"
dependencies = [
"num-conv",
"time-core",
]
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.22" version = "1.0.22"

View File

@@ -6,4 +6,8 @@ edition = "2024"
[dependencies] [dependencies]
colored = "3.0.0" colored = "3.0.0"
display-info = "0.5.7" display-info = "0.5.7"
objc2 = "0.6.3"
objc2-app-kit = "0.3.2"
objc2-foundation = "0.3.2"
plist = "1.8.0"
sysinfo = "0.37.2" sysinfo = "0.37.2"

50
src/helpers/cursor.rs Normal file
View File

@@ -0,0 +1,50 @@
// https://github.com/fastfetch-cli/fastfetch/blob/dev/src/detection/cursor/cursor_apple.m
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");
let mut fill = "Black".to_string();
let mut outline = "White".to_string();
let mut size = "32".to_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);
}
if let Some(o_dict) = dict.get("cursorOutline").and_then(|v| v.as_dictionary()) {
outline = format_color(o_dict);
}
if let Some(s_val) = dict.get("mouseDriverCursorSize").and_then(|v| v.as_real()) {
size = format!("{:.0}", s_val * 32.0);
}
}
}
format!("Fill - {}, Outline - {} ({}px)", fill, outline, size)
}

View File

@@ -0,0 +1,22 @@
// https://github.com/fastfetch-cli/fastfetch/blob/dev/src/detection/theme/theme_apple.c
use sysinfo::System;
pub fn get_desktop_env_info() -> String {
let os_version = System::os_version().unwrap_or_else(|| "0.0.0".to_string());
let major_version: u32 = os_version
.split('.')
.next()
.and_then(|s| s.parse().ok())
.unwrap_or(0);
if major_version > 15 {
"Liquid Glass".to_string()
} else if major_version < 10 && major_version != 0 {
"Platinum".to_string()
} else if major_version >= 10 {
"Aqua".to_string()
} else {
"Unknown".to_string()
}
}

25
src/helpers/font.rs Normal file
View File

@@ -0,0 +1,25 @@
// https://github.com/fastfetch-cli/fastfetch/blob/dev/src/detection/font/font_apple.m
use objc2_app_kit::NSFont;
use objc2_foundation::MainThreadMarker;
pub fn get_font_info() -> String {
let _mtm = MainThreadMarker::new().expect("Must be on the main thread to query fonts");
let sys_font = NSFont::systemFontOfSize(12.0);
let sys_name = sys_font
.familyName()
.expect("System font must have a family name")
.to_string();
let user_font = NSFont::userFontOfSize(12.0);
let user_name = match user_font {
Some(font) => font
.familyName()
.map(|name| name.to_string())
.unwrap_or_else(|| "Helvetica".to_string()),
None => "Helvetica".to_string(),
};
format!("{} [System], {} [User]", sys_name, user_name)
}

View File

@@ -1,4 +1,11 @@
pub mod cursor;
pub mod desktop_env;
pub mod display; pub mod display;
pub mod font;
pub mod packages; pub mod packages;
pub mod shell; pub mod shell;
pub mod terminal;
pub mod terminal_font;
pub mod uptime; pub mod uptime;
pub mod wm;
pub mod wm_theme;

38
src/helpers/terminal.rs Normal file
View File

@@ -0,0 +1,38 @@
use std::env;
use sysinfo::System;
pub fn get_terminal_info() -> String {
let term_env = env::var("TERM_PROGRAM")
.or_else(|_| env::var("TERM"))
.unwrap_or_default();
let term_ver = env::var("TERM_PROGRAM_VERSION").unwrap_or_default();
if !term_env.is_empty() && term_env != "xterm-256color" && term_env != "xterm" {
let clean_name = term_env
.replace("com.apple.", "") // Apple Terminal
.replace("com.mitchellh.", "") // Ghostty
.replace(".app", "");
return if term_ver.is_empty() {
clean_name
} else {
format!("{} {}", clean_name, term_ver)
};
}
let mut sys = System::new();
sys.refresh_processes(sysinfo::ProcessesToUpdate::All, true);
let my_pid = sysinfo::get_current_pid().unwrap_or(sysinfo::Pid::from(0));
if let Some(process) = sys.process(my_pid) {
if let Some(parent_pid) = process.parent() {
if let Some(parent_proc) = sys.process(parent_pid) {
return parent_proc.name().to_string_lossy().replace(".app", "");
}
}
}
"unknown".to_string()
}

View File

@@ -0,0 +1,32 @@
// https://github.com/fastfetch-cli/fastfetch/blob/dev/src/detection/terminalfont/terminalfont.c
use std::process::Command;
pub fn get_terminal_font_info() -> String {
let output = Command::new("ghostty").arg("+show-config").output();
let mut font_family = String::new();
let mut font_size = String::new();
if let Ok(out) = output {
let stdout = String::from_utf8_lossy(&out.stdout);
for line in stdout.lines() {
if line.starts_with("font-family =") && font_family.is_empty() {
font_family = line.replace("font-family =", "").trim().to_string();
}
if line.starts_with("font-size =") && font_size.is_empty() {
font_size = line.replace("font-size =", "").trim().to_string();
}
}
}
if font_family.is_empty() {
font_family = "JetBrainsMono Nerd Font".to_string();
}
if font_size.is_empty() {
font_size = "13".to_string();
}
format!("{} Regular ({}pt)", font_family, font_size)
}

47
src/helpers/wm.rs Normal file
View File

@@ -0,0 +1,47 @@
// https://github.com/fastfetch-cli/fastfetch/blob/dev/src/detection/displayserver/displayserver_apple.c
use plist::Value;
use std::path::Path;
pub struct DisplayServerResult {
pub wm_pretty_name: String,
}
pub fn get_window_manager_info() -> DisplayServerResult {
let mut result = DisplayServerResult {
wm_pretty_name: "Quartz Compositor".to_string(),
};
if cfg!(target_os = "macos") {
let plist_path = "/System/Library/CoreServices/WindowManager.app/Contents/version.plist";
if Path::new(plist_path).exists() {
if let Ok(value) = Value::from_file(plist_path) {
if let Some(dict) = value.as_dictionary() {
if let Some(raw_version) = dict.get("SourceVersion").and_then(|v| v.as_string())
{
// Apple format: AAAABBBCCDDDDDD (Major, Minor, Patch, Build)
if raw_version.len() >= 8 && raw_version.chars().all(|c| c.is_numeric()) {
let major =
raw_version[..raw_version.len() - 12].trim_start_matches('0');
let minor = raw_version[raw_version.len() - 12..raw_version.len() - 9]
.trim_start_matches('0');
let patch = raw_version[raw_version.len() - 9..raw_version.len() - 7]
.trim_start_matches('0');
let m = if minor.is_empty() { "0" } else { minor };
let p = if patch.is_empty() { "0" } else { patch };
result.wm_pretty_name =
format!("Quartz Compositor {}.{}.{}", major, m, p);
} else {
result.wm_pretty_name = format!("Quartz Compositor {}", raw_version);
}
}
}
}
}
}
result
}

40
src/helpers/wm_theme.rs Normal file
View File

@@ -0,0 +1,40 @@
// https://github.com/fastfetch-cli/fastfetch/blob/dev/src/detection/wmtheme/wmtheme_apple.m
use plist::Value;
use std::env;
use std::path::PathBuf;
pub fn get_wm_theme_info() -> String {
let mut path = PathBuf::from(env::var("HOME").unwrap_or_else(|_| "".to_string()));
path.push("Library/Preferences/.GlobalPreferences.plist");
let mut accent_name = "Multicolor".to_string();
let mut appearance = "Light".to_string();
if let Ok(value) = Value::from_file(path) {
if let Some(dict) = value.as_dictionary() {
if let Some(accent_val) = dict
.get("AppleAccentColor")
.and_then(|v| v.as_signed_integer())
{
accent_name = match accent_val {
-1 => "Graphite".to_string(),
0 => "Red".to_string(),
1 => "Orange".to_string(),
2 => "Yellow".to_string(),
3 => "Green".to_string(),
4 => "Blue".to_string(),
5 => "Purple".to_string(),
6 => "Pink".to_string(),
_ => "Multicolor".to_string(),
};
}
if let Some(style) = dict.get("AppleInterfaceStyle").and_then(|v| v.as_string()) {
appearance = style.to_string(); // Usually "Dark"
}
}
}
format!("{} ({})", accent_name, appearance)
}

View File

@@ -2,7 +2,6 @@
// neo64fetch - "jarvis, rewrite this project in rust" // neo64fetch - "jarvis, rewrite this project in rust"
// use colored::*; // use colored::*;
use display_info::DisplayInfo;
use std::env; use std::env;
use sysinfo::System; use sysinfo::System;
mod helpers; mod helpers;
@@ -19,8 +18,15 @@ struct Stats {
packages: String, packages: String,
shell: String, shell: String,
display: String, display: String,
desktop_env: String,
window_manager: String,
window_manager_theme: String,
font: String,
cursor: String,
terminal: String,
terminal_font: String,
// Extra fields which are usually appended // Extra fields
architecture: String, architecture: String,
} }
@@ -41,8 +47,16 @@ fn main() {
packages: helpers::packages::get_brew_info(), packages: helpers::packages::get_brew_info(),
shell: helpers::shell::get_shell_info(), shell: helpers::shell::get_shell_info(),
display: helpers::display::get_display_info(), display: helpers::display::get_display_info(),
desktop_env: helpers::desktop_env::get_desktop_env_info(),
window_manager: helpers::wm::get_window_manager_info().wm_pretty_name,
window_manager_theme: helpers::wm_theme::get_wm_theme_info(),
font: helpers::font::get_font_info(),
cursor: helpers::cursor::get_cursor_info(),
terminal: helpers::terminal::get_terminal_info(),
terminal_font: helpers::terminal_font::get_terminal_font_info(),
}; };
// TODO: Add ascii art support later
// Testing each component separately; going to comment out at the end // Testing each component separately; going to comment out at the end
{ {
println!("{}", stats.username); println!("{}", stats.username);
@@ -55,5 +69,12 @@ fn main() {
println!("{}", stats.packages); println!("{}", stats.packages);
println!("{}", stats.shell); println!("{}", stats.shell);
println!("{}", stats.display); println!("{}", stats.display);
println!("{}", stats.desktop_env);
println!("{}", stats.window_manager);
println!("{}", stats.window_manager_theme);
println!("{}", stats.font);
println!("{}", stats.cursor);
println!("{}", stats.terminal);
println!("{}", stats.terminal_font);
} }
} }