use std::path::PathBuf;
use eyre::Result;
use indexmap::IndexMap;
use super::driver::{self, Action, DriverOpts};
use crate::config::config_file::ConfigFile;
use crate::config::config_file::mise_toml::MiseToml;
use crate::config::{ConfigPathOptions, Settings, resolve_target_config_path};
use crate::file::display_path;
use crate::system;
use crate::system::packages::PackageRequest;
#[derive(Debug, clap::Args)]
#[clap(visible_alias = "u", verbatim_doc_comment, after_long_help = AFTER_LONG_HELP)]
pub struct SystemUse {
#[clap(value_name = "PACKAGE", required = true)]
packages: Vec<String>,
#[clap(long, short, value_name = "ENV", conflicts_with_all = ["global", "path"])]
env: Option<String>,
#[clap(long, short)]
global: bool,
#[clap(long, short = 'n')]
dry_run: bool,
#[clap(long, short, value_name = "PATH", conflicts_with = "global")]
path: Option<PathBuf>,
#[clap(long, short)]
yes: bool,
}
impl SystemUse {
pub async fn run(self) -> Result<()> {
Settings::get().ensure_experimental("mise system")?;
let mut by_mgr: IndexMap<String, Vec<PackageRequest>> = IndexMap::new();
let mut entries: Vec<(String, String)> = vec![];
for spec in &self.packages {
let (mgr, request) = system::parse_use_spec(spec)?;
let key = format!("{mgr}:{}", request.name);
let version = request.version.clone().unwrap_or_else(|| "latest".into());
match entries.iter_mut().find(|(k, _)| k == &key) {
Some(entry) => entry.1 = version,
None => entries.push((key, version)),
}
let requests = by_mgr.entry(mgr).or_default();
match requests.iter_mut().find(|r| r.name == request.name) {
Some(r) => *r = request,
None => requests.push(request),
}
}
let mgrs = system::packages_from_requests(by_mgr)?;
let path = resolve_target_config_path(ConfigPathOptions {
global: self.global,
path: self.path.clone(),
env: self.env.clone(),
cwd: None,
prefer_toml: true, prevent_home_local: true, })?;
if self.dry_run {
for (key, version) in &entries {
miseprintln!("{}: \"{key}\" = \"{version}\"", display_path(&path));
}
} else {
let mut cf = if path.exists() {
MiseToml::from_file(&path)?
} else {
MiseToml::init(&path)
};
for (key, version) in &entries {
cf.update_system_package(key, version)?;
}
cf.save()?;
info!(
"{}: added {}",
display_path(&path),
entries
.iter()
.map(|(k, _)| k.as_str())
.collect::<Vec<_>>()
.join(", ")
);
}
if !self.dry_run {
for mp in &mgrs {
if !mp.disabled && !mp.manager.is_available() {
info!(
"{}: {} — added to config without installing",
mp.manager.name(),
mp.manager.unavailable_reason()
);
}
}
}
let opts = DriverOpts {
manager: None,
explicit: false,
dry_run: self.dry_run,
update: false,
yes: self.yes,
};
driver::run(mgrs, Action::Install, &opts).await
}
}
static AFTER_LONG_HELP: &str = color_print::cstr!(
r#"<bold><underline>Examples:</underline></bold>
$ <bold>mise system use apt:curl brew:jq</bold>
$ <bold>mise system use -g brew:postgresql@17</bold>
$ <bold>mise system use apt:curl@8.5.0-2</bold>
"#
);