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..
-### 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..6030eb5
--- /dev/null
+++ b/src/packages/pacman.rs
@@ -0,0 +1,10 @@
+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_type().is_ok_and(|v| v.is_dir()))
+ .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)
+}