use std::{fs::File, io::Read};
use pacman::Pacman;
use portage::Portage;
use serde::{Deserialize, Serialize};
use crate::error::{Error, Result};
#[cfg(feature = "pacman")]
mod pacman;
#[cfg(feature = "portage")]
mod portage;
#[derive(Debug, Serialize, Deserialize)]
pub struct PackageList {
packages: Vec<Package>,
manager: Manager,
}
impl PackageList {
pub fn install(&self) -> Result<()> {
self.manager.to_package_manager().install(self.packages.clone())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Package {
pub id: String,
pub version: String,
pub explicit: bool,
}
#[derive(Debug, Clone, clap::ValueEnum, Serialize, Deserialize)]
pub enum Manager {
#[cfg(feature = "pacman")]
Pacman,
#[cfg(feature = "portage")]
Portage,
}
impl Manager {
pub fn get_manager(manager: Option<Manager>) -> Result<Box<dyn PackageManager>> {
#[cfg(not(target_os = "linux"))]
{
return Err(Error::Unsupported);
}
#[cfg(target_os = "linux")]
{
if let Some(man) = manager {
return Ok(man.to_package_manager());
}
let mut os_release = File::open("/etc/os-release")?;
let mut content = String::new();
os_release.read_to_string(&mut content)?;
let lines: Vec<&str> = content.split('\n').collect();
for line in lines {
let Some((key, value)) = line.split_once('=') else {
continue;
};
if key == "ID" {
return Self::from_str(value);
}
}
Err(Error::Unsupported)
}
}
fn from_str(value: &str) -> Result<Box<dyn PackageManager>> {
Ok(match value {
#[cfg(feature = "pacman")]
"arch" => Box::new(Pacman),
#[cfg(feature = "portage")]
"gentoo" => Box::new(Portage),
_ => return Err(Error::Unsupported),
})
}
fn to_package_manager(&self) -> Box<dyn PackageManager> {
match self {
#[cfg(feature = "pacman")]
Self::Pacman => Box::new(Pacman),
#[cfg(feature = "portage")]
Self::Portage => Box::new(Portage),
}
}
}
pub trait PackageManager {
fn get_installed(&self) -> Result<PackageList>;
fn install(&self, pkgs: Vec<Package>) -> Result<()>;
}