Documentation
#[derive(Debug, Clone)]
pub struct Packages {
    snap: usize,
    dpkg: usize,
    pacman: usize,
    scoop: usize,
    opkg: usize,
}

impl Display for Packages {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let mut v = Vec::new();
        if self.dpkg > 0 {
            v.push(format!("{} (dpkg)", self.dpkg))
        }
        if self.snap > 0 {
            v.push(format!("{} (snap)", self.snap))
        }
        if self.pacman > 0 {
            v.push(format!("{} (pacman)", self.pacman))
        }
        if self.scoop > 0 {
            v.push(format!("{} (scoop)", self.scoop))
        }
        if self.opkg > 0 {
            v.push(format!("{} (opkg)", self.opkg))
        }

        f.write_str(&v.join(", "))
    }
}

use std::fmt::Display;

use crate::share::exec;

fn pacman() -> Option<usize> {
    #[cfg(unix)]
    let dir = std::path::Path::new("/var/lib/pacman/local");
    #[cfg(windows)]
    let dir = std::path::Path::new(&std::env::var("MSYSTEM_PREFIX").ok()?.to_string())
        .parent()
        .unwrap_or(std::path::Path::new("/"))
        .join("var/lib/pacman/local");

    Some(std::fs::read_dir(dir).ok()?.count().saturating_sub(1))
}
fn snap() -> Option<usize> {
    #[cfg(unix)]
    let dir = std::path::Path::new("/var/lib/snapd/snaps");
    #[cfg(windows)]
    let dir = std::path::Path::new(&std::env::var("MSYSTEM_PREFIX").ok()?.to_string())
        .parent()
        .unwrap_or(std::path::Path::new("/"))
        .join("var/lib/snapd/snaps");

    Some(std::fs::read_dir(dir).ok()?.count().saturating_sub(1))
}

fn scoop() -> Option<usize> {
    let home_dir = dirs::home_dir()?;
    let dir = home_dir.join("scoop").join("apps");

    Some(std::fs::read_dir(dir).ok()?.count().saturating_sub(1))
}

fn dpkg() -> Option<usize> {
    #[cfg(unix)]
    let dir = std::path::Path::new("/var/lib/dpkg/status");
    #[cfg(windows)]
    let dir = std::path::Path::new(&std::env::var("MSYSTEM_PREFIX").ok()?.to_string())
        .parent()
        .unwrap_or(std::path::Path::new("/"))
        .join("var/lib/dpkg/status");
    let file = std::fs::read_to_string(dir).ok()?;
    let mut package_count = 0;
    for line in file.lines() {
        if line.starts_with("Package:") {
            package_count += 1;
        }
    }
    Some(package_count)
}

fn opkg() -> Option<usize> {
    exec("opkg", ["list-installed"]).map(|i| i.lines().count())
}

pub async fn get_packages() -> crate::error::Result<Packages> {
    let packages = tokio::task::spawn_blocking(|| Packages {
        snap: snap().unwrap_or_default(),
        dpkg: dpkg().unwrap_or_default(),
        pacman: pacman().unwrap_or_default(),
        scoop: scoop().unwrap_or_default(),
        opkg: opkg().unwrap_or_default(),
    })
    .await?;

    Ok(packages)
}