use crate::{
environment as e,
system::SystemInput,
unit::{self, SystemUnit},
};
use anyhow::{anyhow, Error};
use std::collections::{BTreeSet, HashSet};
use std::fmt;
system_struct! {
#[doc = "Builds one unit for every batch of packages to install."]
Install {
#[doc="Hierarchy key to lookup for packages to install."]
#[serde(default = "default_key")]
pub key: String,
#[doc="Package provider to use."]
pub provider: Option<String>,
}
}
fn default_key() -> String {
String::from("packages")
}
impl Install {
system_defaults!(translate);
pub fn apply<E>(&self, input: SystemInput<E>) -> Result<Vec<SystemUnit>, Error>
where
E: Copy + e::Environment,
{
let SystemInput {
packages,
data,
allocator,
state,
..
} = input;
let mut units = Vec::new();
let provider = self.provider.as_deref();
let package_manager = match provider {
Some(provider) => packages.get(provider)?,
None => packages.default(),
};
let id = self
.id
.as_deref()
.map(|id| id.to_string())
.or_else(|| provider.map(|id| id.to_string()))
.or_else(|| packages.default().map(|p| p.name().to_string()))
.ok_or_else(|| anyhow!("no usable install provider id"))?;
let mut all_packages = BTreeSet::new();
let key = match package_manager.as_deref().and_then(|p| p.key()) {
Some(key) => key.to_string(),
None => match provider {
Some(provider) => format!("{}::{}", provider, self.key),
None => self.key.to_string(),
},
};
all_packages.extend(data.load_or_default::<Vec<String>>(&key)?);
if state.is_hash_fresh(&id, &all_packages)? {
log::trace!("Skipping `{}` since hash is fresh", id);
return Ok(units);
}
let package_manager = match package_manager {
Some(package_manager) => package_manager,
None => {
if !all_packages.is_empty() {
return Ok(units);
}
match provider {
Some(provider) => {
log::warn!("No package manager for provider `{}` found", provider)
}
None => log::warn!("No primary package manager found"),
}
return Ok(units);
}
};
let mut to_install = all_packages.iter().cloned().collect::<HashSet<_>>();
for package in package_manager.list_packages()? {
to_install.remove(&package.name);
}
let to_install = to_install.into_iter().collect();
let thread_local = package_manager.needs_interaction();
let mut unit = allocator.unit(unit::Install {
package_manager,
all_packages,
to_install,
id,
});
unit.thread_local = thread_local;
units.push(unit);
Ok(units)
}
}
impl fmt::Display for Install {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self.provider.as_ref() {
Some(provider) => write!(fmt, "install packages using provider `{}`", provider),
None => write!(fmt, "install packages using primary provider"),
}
}
}