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, 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) -> Result> { #[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> { 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 { 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; fn install(&self, pkgs: Vec) -> Result<()>; }