From 71bf46cc039251521ac297ae5890492321db579c Mon Sep 17 00:00:00 2001 From: camtisocial Date: Sun, 22 Feb 2026 21:11:54 -0800 Subject: [PATCH] added --json, --color, --image flags --- Cargo.lock | 1 + Cargo.toml | 1 + src/main.rs | 91 +++++++++++++++++++++++++++++++++++++++++++++++++--- src/stats.rs | 14 ++++++++ 4 files changed, 103 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5e6bd39..2068594 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1247,6 +1247,7 @@ dependencies = [ "raur", "reqwest 0.12.26", "serde", + "serde_json", "tokio", "toml", ] diff --git a/Cargo.toml b/Cargo.toml index c371b3d..804d523 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ nix = { version = "0.29", features = ["fs"] } raur = "8" reqwest = { version = "0.12", features = ["blocking"] } serde = { version = "1", features = ["derive"] } +serde_json = "1" tokio = { version = "1", features = ["rt"] } toml = "0.8" onefetch-image = "2" diff --git a/src/main.rs b/src/main.rs index 5debb71..0c32aeb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -38,6 +38,9 @@ Commands: Options: --ascii Use custom ASCII art (path, built-in name, or NONE) + --color Override ASCII art color (name, hex #RRGGBB, or none) + --image Use an image instead of ASCII art + --json Output stats as JSON --local Use local cached database (skip temp sync) -d, --debug Debug mode -h, --help Print help @@ -69,6 +72,15 @@ struct Cli { #[arg(long = "ascii", hide = true)] ascii: Option, + #[arg(long = "color", hide = true)] + color: Option, + + #[arg(long = "image", hide = true)] + image: Option, + + #[arg(long = "json", hide = true)] + json: bool, + #[arg(long = "yay", hide = true)] yay: bool, @@ -109,16 +121,21 @@ fn main() { // Load config let mut config = Config::load(); - // Override ascii from CLI if let Some(ref ascii) = cli.ascii { config.display.ascii = ascii.clone(); } - // Apply default_args if no meaningful flags were provided + if let Some(ref color) = cli.color { + config.display.ascii_color = color.clone(); + } + + if let Some(ref image) = cli.image { + config.display.image = image.clone(); + } + let cli = if is_bare_invocation(&cli) && !config.default_args.is_empty() { let mut args = vec!["pacfetch".to_string()]; args.extend(config.default_args.split_whitespace().map(String::from)); - // Carry forward any explicit flags that were set if cli.debug { args.push("-d".to_string()); } @@ -126,6 +143,17 @@ fn main() { args.push("--ascii".to_string()); args.push(ascii.clone()); } + if let Some(ref color) = cli.color { + args.push("--color".to_string()); + args.push(color.clone()); + } + if let Some(ref image) = cli.image { + args.push("--image".to_string()); + args.push(image.clone()); + } + if cli.json { + args.push("--json".to_string()); + } match Cli::try_parse_from(&args) { Ok(cli) => cli, Err(_) => { @@ -193,6 +221,14 @@ fn main() { ); spinner.finish_and_clear(); stats + } else if cli.json { + pacman::get_stats( + &config.display.parsed_stats(), + false, + fresh_sync, + &config, + None, + ) } else if cli.debug { println!(); pacman::get_stats( @@ -215,10 +251,57 @@ fn main() { stats }; - if cli.debug { + if cli.json { + println!("{}", stats_to_json_string(&stats)); + } else if cli.debug { ui::display_stats(&stats, &config); println!(); } else if let Err(e) = ui::display_stats_with_graphics(&stats, &config) { eprintln!("error: {}", e); } } + +fn stats_to_json_string(stats: &pacman::PacmanStats) -> String { + let mut map = serde_json::Map::new(); + for id in stats::ALL_STAT_IDS { + if let Some(value) = id.format_value(stats) { + map.insert( + id.config_key().to_string(), + serde_json::Value::String(value), + ); + } + } + serde_json::to_string_pretty(&serde_json::Value::Object(map)) + .unwrap_or_else(|_| "{}".to_string()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::pacman::PacmanStats; + + #[test] + fn test_json_contains_installed_and_upgradable() { + let stats = PacmanStats { + total_installed: 1234, + total_upgradable: 5, + ..Default::default() + }; + let output = stats_to_json_string(&stats); + let parsed: serde_json::Value = serde_json::from_str(&output).unwrap(); + assert_eq!(parsed["installed"], "1234"); + assert_eq!(parsed["upgradable"], "5"); + } + + #[test] + fn test_json_omits_none_values() { + let stats = PacmanStats { + total_installed: 100, + total_upgradable: 0, + ..Default::default() + }; + let output = stats_to_json_string(&stats); + let parsed: serde_json::Value = serde_json::from_str(&output).unwrap(); + assert!(parsed.get("download_size").is_none()); + } +} diff --git a/src/stats.rs b/src/stats.rs index 27355aa..86b5939 100644 --- a/src/stats.rs +++ b/src/stats.rs @@ -20,6 +20,20 @@ pub enum StatId { Disk, } +pub const ALL_STAT_IDS: &[StatId] = &[ + StatId::Installed, + StatId::Upgradable, + StatId::LastUpdate, + StatId::DownloadSize, + StatId::InstalledSize, + StatId::NetUpgradeSize, + StatId::OrphanedPackages, + StatId::CacheSize, + StatId::MirrorUrl, + StatId::MirrorHealth, + StatId::Disk, +]; + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum PaletteVariant { Both,