From 3e59da1913a369cd24d1953549b883bde73af994 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 7 Oct 2026 17:04:11 +0000 Subject: [PATCH 1/2] refactor: flatten package managers into plain functions - Replace the PackageManager trait/structs with src/packages/, one count() function per manager - Show every detected package manager instead of picking one by distro: a single one prints just the count, several print "xbps(632), apk(251)" - Print "Unknown" instead of panicking when none is found - Drop the package manager list from README Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Tcei1A4L5TW8AskmMbjWFQ --- README.md | 7 --- src/main.rs | 9 ++-- src/package_managers/alpine.rs | 31 ------------- src/package_managers/emerge.rs | 28 ------------ src/package_managers/mod.rs | 58 ------------------------- src/package_managers/package_manager.rs | 7 --- src/package_managers/pacman.rs | 34 --------------- src/package_managers/xbps.rs | 40 ----------------- src/packages/apk.rs | 9 ++++ src/packages/emerge.rs | 9 ++++ src/packages/mod.rs | 47 ++++++++++++++++++++ src/packages/pacman.rs | 14 ++++++ src/packages/xbps.rs | 14 ++++++ 13 files changed, 96 insertions(+), 211 deletions(-) delete mode 100644 src/package_managers/alpine.rs delete mode 100644 src/package_managers/emerge.rs delete mode 100644 src/package_managers/mod.rs delete mode 100644 src/package_managers/package_manager.rs delete mode 100644 src/package_managers/pacman.rs delete mode 100644 src/package_managers/xbps.rs create mode 100644 src/packages/apk.rs create mode 100644 src/packages/emerge.rs create mode 100644 src/packages/mod.rs create mode 100644 src/packages/pacman.rs create mode 100644 src/packages/xbps.rs diff --git a/README.md b/README.md index e2ca4b2..172e636 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,6 @@ However, maintaining clean code is also a priority..
image -### supports the following package managers - -- xbps ( [Void Linux](https://voidlinux.org/) ) -- apk ( [Alpine Linux](https://wiki.alpinelinux.org/wiki/Main_Page) ) -- pacman ( [Arch Linux](https://archlinux.org/) ) -- emerge ( [Gentoo Linux](https://www.gentoo.org/) ) - btw, i'll be happy if somebody send some cool ascii cat arts in issues :point_right::point_left: ### installation diff --git a/src/main.rs b/src/main.rs index f372060..ff62546 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,8 @@ mod host; mod memory; -mod package_managers; +mod packages; mod uptime; -use crate::{host::get_host, package_managers::PackageManagers}; +use crate::host::get_host; use colored::*; #[cfg(not(unix))] @@ -27,10 +27,7 @@ fn main() { host = get_host().unwrap_or("Unknown".to_owned()), uptime = uptime::get().unwrap_or_default(), memory = memory::get().unwrap_or_default(), - pkgs = PackageManagers::new() - .count_pkgs_by_distro() - .expect("Failed to get package manager for this distro !") - .expect("Failed to count packages in this distro !"), + pkgs = packages::get().unwrap_or("Unknown".to_owned()), os_field = wrap_field("os"), host_field = wrap_field("host"), memory_field = wrap_field("memory"), diff --git a/src/package_managers/alpine.rs b/src/package_managers/alpine.rs deleted file mode 100644 index 9f0ffdc..0000000 --- a/src/package_managers/alpine.rs +++ /dev/null @@ -1,31 +0,0 @@ -use std::{ - fs::{self, File}, - io::{self, BufRead}, -}; - -use super::package_manager::PackageManager; - -const ALPINE_INSTALLED: &str = "/lib/apk/db/installed"; - -#[derive(Clone, Debug)] -pub struct AlpinePackageManager {} -impl AlpinePackageManager { - const RELATIVE_DISTRO_IDS: [&str; 1] = ["alpine"]; -} -impl PackageManager for AlpinePackageManager { - fn is_relative_distro_id(&self, id: &str) -> bool { - Self::RELATIVE_DISTRO_IDS.contains(&id) - } - - fn count_packages(&self) -> io::Result { - Ok(io::BufReader::new(File::open(ALPINE_INSTALLED)?) - .lines() - .map_while(Result::ok) - .filter(|line| line.starts_with("P:")) - .count() as u64) - } - - fn is_available(&self) -> bool { - fs::metadata(ALPINE_INSTALLED).is_ok_and(|v| v.is_file()) - } -} diff --git a/src/package_managers/emerge.rs b/src/package_managers/emerge.rs deleted file mode 100644 index 2b6a41e..0000000 --- a/src/package_managers/emerge.rs +++ /dev/null @@ -1,28 +0,0 @@ -use std::{fs, io}; - -use super::package_manager::PackageManager; - -const EMERGE_DIR: &str = "/var/db/pkg"; - -#[derive(Clone, Debug)] -pub struct EmergePackageManager {} -impl EmergePackageManager { - const RELATIVE_DISTRO_IDS: [&str; 1] = ["gentoo"]; -} -impl PackageManager for EmergePackageManager { - fn is_relative_distro_id(&self, id: &str) -> bool { - Self::RELATIVE_DISTRO_IDS.contains(&id) - } - - fn count_packages(&self) -> io::Result { - Ok(fs::read_dir(EMERGE_DIR)? - .flatten() - .filter(|entry| entry.metadata().is_ok_and(|v| v.is_dir())) - .flat_map(|v| fs::read_dir(v.path()).map(|v| v.count())) - .fold(0, |acc, count| acc + count as u64)) - } - - fn is_available(&self) -> bool { - fs::metadata(EMERGE_DIR).is_ok_and(|v| v.is_dir()) - } -} diff --git a/src/package_managers/mod.rs b/src/package_managers/mod.rs deleted file mode 100644 index 4397676..0000000 --- a/src/package_managers/mod.rs +++ /dev/null @@ -1,58 +0,0 @@ -mod alpine; -mod emerge; -mod package_manager; -mod pacman; -mod xbps; -use std::vec; -use std::{ - fs::File, - io::{self, BufRead}, -}; - -use package_manager::PackageManager; - -use self::alpine::AlpinePackageManager; -use self::emerge::EmergePackageManager; -use self::pacman::PacmanPackageManager; -use self::xbps::XBPSPackageManager; - -const OS_RELEASE_FILE: &str = "/etc/os-release"; - -const XBPS: XBPSPackageManager = XBPSPackageManager {}; -const PACMAN: PacmanPackageManager = PacmanPackageManager {}; -const EMERGE: EmergePackageManager = EmergePackageManager {}; -const ALPINE: AlpinePackageManager = AlpinePackageManager {}; - -pub struct PackageManagers { - managers: Vec<&'static dyn PackageManager>, -} -impl PackageManagers { - pub fn new() -> Self { - Self { - managers: vec![&XBPS, &PACMAN, &EMERGE, &ALPINE], - } - } - - pub fn count_pkgs_by_distro(&self) -> Option> { - io::BufReader::new(File::open(OS_RELEASE_FILE).expect("Failed to open os-release file !")) - .lines() - .map_while(Result::ok) - .filter(|line| line.starts_with("ID") || line.starts_with("ID_LIKE")) - .find_map(|line| { - let distro_id = line.split_once('=').map(|(_, id)| id.trim_matches('"'))?; - - self.managers - .iter() - .find(|manager| manager.is_relative_distro_id(distro_id)) - .and_then(|v| v.is_available().then_some(v.count_packages())) - }) - } - - #[allow(dead_code)] - pub fn count_pkgs_by_available_manager(&self) -> Option> { - self.managers - .iter() - .find(|v| v.is_available()) - .map(|v| v.count_packages()) - } -} diff --git a/src/package_managers/package_manager.rs b/src/package_managers/package_manager.rs deleted file mode 100644 index 634b0ca..0000000 --- a/src/package_managers/package_manager.rs +++ /dev/null @@ -1,7 +0,0 @@ -use std::io; - -pub trait PackageManager { - fn count_packages(&self) -> io::Result; - fn is_available(&self) -> bool; - fn is_relative_distro_id(&self, id: &str) -> bool; -} diff --git a/src/package_managers/pacman.rs b/src/package_managers/pacman.rs deleted file mode 100644 index be8e909..0000000 --- a/src/package_managers/pacman.rs +++ /dev/null @@ -1,34 +0,0 @@ -use std::os::unix::ffi::OsStrExt; -use std::{fs, io}; - -use super::package_manager::PackageManager; - -const PACMAN_DIR: &str = "/var/lib/pacman/local"; - -#[derive(Clone, Debug)] -pub struct PacmanPackageManager {} -impl PacmanPackageManager { - const RELATIVE_DISTRO_IDS: [&str; 2] = ["arch", "artix"]; -} -impl PackageManager for PacmanPackageManager { - fn is_relative_distro_id(&self, id: &str) -> bool { - Self::RELATIVE_DISTRO_IDS.contains(&id) - } - - fn count_packages(&self) -> io::Result { - Ok(fs::read_dir(PACMAN_DIR)? - .flatten() - .filter(|entry| { - entry - .file_name() - .as_bytes() - .iter() - .all(u8::is_ascii_lowercase) - }) - .count() as u64) - } - - fn is_available(&self) -> bool { - fs::metadata(PACMAN_DIR).is_ok_and(|v| v.is_dir()) - } -} diff --git a/src/package_managers/xbps.rs b/src/package_managers/xbps.rs deleted file mode 100644 index 94e1a4d..0000000 --- a/src/package_managers/xbps.rs +++ /dev/null @@ -1,40 +0,0 @@ -use std::{ - fs::{self, File}, - io::{self, BufRead}, -}; - -use super::package_manager::PackageManager; - -const XBPS_DIR: &str = "/var/db/xbps"; - -#[derive(Clone, Debug)] -pub struct XBPSPackageManager {} -impl XBPSPackageManager { - const RELATIVE_DISTRO_IDS: [&str; 1] = ["void"]; -} -impl PackageManager for XBPSPackageManager { - fn is_relative_distro_id(&self, id: &str) -> bool { - Self::RELATIVE_DISTRO_IDS.contains(&id) - } - - fn count_packages(&self) -> io::Result { - let file = fs::read_dir(XBPS_DIR)? - .flatten() - .find(|file| { - file.file_name() - .to_str() - .is_some_and(|f| f.starts_with("pkgdb-")) - }) - .expect("Failed to locate the pkgdb file !"); - - Ok(io::BufReader::new(File::open(file.path())?) - .lines() - .map_while(Result::ok) - .filter(|line| line.contains("repository")) - .count() as u64) - } - - fn is_available(&self) -> bool { - fs::metadata(XBPS_DIR).is_ok_and(|v| v.is_dir()) - } -} diff --git a/src/packages/apk.rs b/src/packages/apk.rs new file mode 100644 index 0000000..90cf0ef --- /dev/null +++ b/src/packages/apk.rs @@ -0,0 +1,9 @@ +use std::io; + +use super::read_lines; + +pub fn count() -> io::Result { + Ok(read_lines("/lib/apk/db/installed")? + .filter(|line| line.starts_with("P:")) + .count() as u64) +} diff --git a/src/packages/emerge.rs b/src/packages/emerge.rs new file mode 100644 index 0000000..b5c7a55 --- /dev/null +++ b/src/packages/emerge.rs @@ -0,0 +1,9 @@ +use std::{fs, io}; + +pub fn count() -> io::Result { + Ok(fs::read_dir("/var/db/pkg")? + .flatten() + .filter(|entry| entry.metadata().is_ok_and(|v| v.is_dir())) + .flat_map(|category| fs::read_dir(category.path()).map(|v| v.count() as u64)) + .sum()) +} diff --git a/src/packages/mod.rs b/src/packages/mod.rs new file mode 100644 index 0000000..9f009ea --- /dev/null +++ b/src/packages/mod.rs @@ -0,0 +1,47 @@ +mod apk; +mod emerge; +mod pacman; +mod xbps; + +use std::{ + fs::File, + io::{self, BufRead, BufReader}, + path::Path, +}; + +type Count = fn() -> io::Result; + +const MANAGERS: [(&str, Count); 4] = [ + ("xbps", xbps::count), + ("apk", apk::count), + ("pacman", pacman::count), + ("emerge", emerge::count), +]; + +// Counts installed packages of every package manager found on the system. +// A single manager is shown as just the count, several as "xbps(632), apk(251)". +pub fn get() -> io::Result { + let counts: Vec<_> = MANAGERS + .iter() + .filter_map(|(name, count)| count().ok().map(|count| (name, count))) + .collect(); + + match counts.as_slice() { + [] => Err(io::Error::new( + io::ErrorKind::NotFound, + "no supported package manager found", + )), + [(_, count)] => Ok(count.to_string()), + _ => Ok(counts + .iter() + .map(|(name, count)| format!("{name}({count})")) + .collect::>() + .join(", ")), + } +} + +fn read_lines(path: impl AsRef) -> io::Result> { + Ok(BufReader::new(File::open(path)?) + .lines() + .map_while(Result::ok)) +} diff --git a/src/packages/pacman.rs b/src/packages/pacman.rs new file mode 100644 index 0000000..03cf1a3 --- /dev/null +++ b/src/packages/pacman.rs @@ -0,0 +1,14 @@ +use std::{fs, io, os::unix::ffi::OsStrExt}; + +pub fn count() -> io::Result { + Ok(fs::read_dir("/var/lib/pacman/local")? + .flatten() + .filter(|entry| { + entry + .file_name() + .as_bytes() + .iter() + .all(u8::is_ascii_lowercase) + }) + .count() as u64) +} diff --git a/src/packages/xbps.rs b/src/packages/xbps.rs new file mode 100644 index 0000000..439f89f --- /dev/null +++ b/src/packages/xbps.rs @@ -0,0 +1,14 @@ +use std::{fs, io, os::unix::ffi::OsStrExt}; + +use super::read_lines; + +pub fn count() -> io::Result { + let pkgdb = fs::read_dir("/var/db/xbps")? + .flatten() + .find(|entry| entry.file_name().as_bytes().starts_with(b"pkgdb-")) + .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "xbps pkgdb not found"))?; + + Ok(read_lines(pkgdb.path())? + .filter(|line| line.contains("repository")) + .count() as u64) +} From 7d09fe05bbab2a9ba65caa576c1b5debe94992f5 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 7 Oct 2026 17:08:46 +0000 Subject: [PATCH 2/2] fix: count pacman packages by directory Package entries are named like bash-5.2.026-2, so the all-lowercase filter skipped almost all of them. Count directories instead, which leaves out only the ALPM_DB_VERSION file. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Tcei1A4L5TW8AskmMbjWFQ --- src/packages/pacman.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/packages/pacman.rs b/src/packages/pacman.rs index 03cf1a3..6030eb5 100644 --- a/src/packages/pacman.rs +++ b/src/packages/pacman.rs @@ -1,14 +1,10 @@ -use std::{fs, io, os::unix::ffi::OsStrExt}; +use std::{fs, io}; +// Every installed package is a "--" directory; +// the only other entry is the ALPM_DB_VERSION file. pub fn count() -> io::Result { Ok(fs::read_dir("/var/lib/pacman/local")? .flatten() - .filter(|entry| { - entry - .file_name() - .as_bytes() - .iter() - .all(u8::is_ascii_lowercase) - }) + .filter(|entry| entry.file_type().is_ok_and(|v| v.is_dir())) .count() as u64) }