use crate::{
config::{Config, PkgConfig},
error::Error,
output::{Output, PkgEntry, PkgEntryState, PkgInstallHints},
pkg_manager,
};
#[derive(Debug, Clone, Default)]
pub struct Options {
pub pattern: Vec<String>,
pub all: bool,
pub all_hints: bool,
pub verbose: bool,
}
pub fn push(config: &Config, opts: Options, output: &mut dyn Output) -> Result<(), Error> {
let manager = pkg_manager::detect();
if manager.is_none() {
output.push_pkg_entry(PkgEntry::NoPackageManagerDetected {
supported: vec!["brew".to_string()],
})?;
}
let home = config.home();
let configured_shell = config.shell();
let needles: Vec<String> = opts.pattern.iter().map(|p| p.to_lowercase()).collect();
let mut entries: Vec<(&str, &smol_str::SmolStr, &PkgConfig)> = config
.pkgs()
.iter()
.filter(|(_, p)| opts.all || !p.is_disabled())
.filter(|(_, p)| p.supports_current_os())
.filter(|(_, p)| p.supports_shell(configured_shell))
.map(|(key, pkg)| (pkg.name.as_deref().unwrap_or(key.as_str()), key, pkg))
.filter(|(label, key, _)| {
needles.is_empty()
|| needles.iter().any(|n| {
label.to_lowercase().contains(n) || key.as_str().to_lowercase().contains(n)
})
})
.collect();
entries.sort_by_key(|(label, _, _)| *label);
let mut aggregate_packages: Vec<String> = Vec::new();
for (label, key, pkg) in entries {
let state = if pkg.is_disabled() {
PkgEntryState::Disabled
} else if pkg.is_installed(home, config.system_inputs()) {
PkgEntryState::Installed
} else {
PkgEntryState::Missing
};
let matched_detect = if opts.verbose {
pkg.matched_detect(home, config.system_inputs())
.map(|d| d.to_string())
} else {
None
};
let install_hints = if !matches!(state, PkgEntryState::Missing) {
PkgInstallHints::default()
} else if opts.all_hints {
PkgInstallHints {
brew: pkg.install_hint.brew.packages.clone(),
}
} else if let Some(mgr) = manager {
let pkgs = mgr.packages_for(&pkg.install_hint);
if pkgs.is_empty() {
PkgInstallHints::default()
} else {
aggregate_packages.extend(pkgs.iter().cloned());
PkgInstallHints {
brew: pkgs.to_vec(),
}
}
} else {
PkgInstallHints::default()
};
output.push_pkg_entry(PkgEntry::Pkg {
name: smol_str::SmolStr::new(label),
key: key.clone(),
description: pkg.description.clone(),
state,
matched_detect,
install_hints,
})?;
}
if !opts.all_hints
&& let Some(mgr) = manager
&& !aggregate_packages.is_empty()
{
output.push_pkg_entry(PkgEntry::AggregateInstall {
pkg_manager: mgr.name().to_string(),
command: mgr.install_command(&aggregate_packages),
packages: aggregate_packages,
})?;
}
Ok(())
}