mod cargo;
mod debian;
mod fedora;
mod python;
mod ruby;
mod rustup_components;
mod rustup_toolchains;
mod winget;
use crate::facts::{self, Facts};
use anyhow::{bail, Error};
use log::warn;
use std::fmt;
use std::sync::Arc;
#[derive(Debug)]
pub struct Package {
pub name: String,
}
pub struct Provider {
default: Option<Arc<dyn PackageManager>>,
}
impl Provider {
pub fn default(&self) -> Option<Arc<dyn PackageManager>> {
self.default.as_ref().map(Arc::clone)
}
pub fn get(&self, name: &str) -> Result<Option<Arc<dyn PackageManager>>, Error> {
if let Some(default) = self.default.as_ref() {
if default.name() == name {
return Ok(Some(Arc::clone(default)));
}
}
match name {
"debian" => test(debian::PackageManager::new()),
"fedora" => test(fedora::PackageManager::new()),
"pip" => test(python::PackageManager::new("pip")),
"pip3" => test(python::PackageManager::new("pip3")),
"gem" => test(ruby::PackageManager::new()),
"cargo" => test(cargo::PackageManager::new()),
"winget" => test(winget::PackageManager::new()),
"rust toolchains" => test(rustup_toolchains::PackageManager::new()),
"rust components" => test(rustup_components::PackageManager::new()),
_ => bail!("No package manager provider for `{}`", name),
}
}
}
pub fn detect(facts: &Facts) -> Result<Provider, Error> {
let default = if let Some(default) = by_distro(facts)? {
Some(default)
} else {
by_os(facts)?
};
Ok(Provider { default })
}
fn by_distro(facts: &Facts) -> Result<Option<Arc<dyn PackageManager>>, Error> {
let distro = match facts.get(facts::DISTRO) {
None => return Ok(None),
Some(distro) => distro,
};
match distro {
"debian" => test(debian::PackageManager::new()),
"fedora" => test(fedora::PackageManager::new()),
distro => {
warn!("no package integration for distro: {}", distro);
Ok(None)
}
}
}
fn by_os(facts: &Facts) -> Result<Option<Arc<dyn PackageManager>>, Error> {
let os = match facts.get(facts::OS) {
None => return Ok(None),
Some(os) => os,
};
match os {
"windows" => test(winget::PackageManager::new()),
os => {
warn!("no package integration for os: {}", os);
Ok(None)
}
}
}
fn test(manager: impl PackageManager + 'static) -> Result<Option<Arc<dyn PackageManager>>, Error> {
if manager.test()? {
Ok(Some(Arc::new(manager)))
} else {
Ok(None)
}
}
pub trait PackageManager: fmt::Debug + Sync + Send {
fn primary(&self) -> bool {
false
}
fn needs_interaction(&self) -> bool {
false
}
fn key(&self) -> Option<&str> {
None
}
fn name(&self) -> &str;
fn test(&self) -> Result<bool, Error>;
fn list_packages(&self) -> Result<Vec<Package>, Error>;
fn install_packages(&self, packages: &[String]) -> Result<(), Error>;
}