use eyre::Result;
use crate::config::{Config, Settings};
use crate::system;
use crate::system::history::OperationScope;
use crate::ui::prompt;
#[derive(Debug, usage_rs::Args)]
#[usage(
verbatim_doc_comment,
example(
r###"mise bootstrap dotfiles unapply
mise bootstrap dotfiles unapply ~/.zshrc
mise bootstrap dotfiles unapply --dry-run
mise bootstrap dotfiles unapply --force --yes"###
)
)]
pub(crate) struct DotfilesUnapply {
#[usage(value_name = "TARGET")]
targets: Vec<String>,
#[usage(long, short)]
force: bool,
#[usage(long, short = 'n')]
dry_run: bool,
#[usage(long, short)]
yes: bool,
}
impl DotfilesUnapply {
pub(crate) async fn run(self) -> Result<()> {
OperationScope::wrap("bootstrap dotfiles unapply", self.dry_run, self.run_inner()).await
}
async fn run_inner(self) -> Result<()> {
let config = Config::get().await?;
let all_files = system::files::files_from_config(&config)?;
let files = all_files
.iter()
.filter(|req| {
system::files::matches_target(&req.target, &req.target_raw, &self.targets)
})
.cloned()
.collect::<Vec<_>>();
let all_edits = system::edits::edits_from_config(&config)?;
let edits = all_edits
.iter()
.filter(|req| system::edits::matches_target(req, &self.targets))
.cloned()
.collect::<Vec<_>>();
if files.is_empty()
&& edits.is_empty()
&& !self.targets.is_empty()
&& (!all_files.is_empty() || !all_edits.is_empty())
{
eyre::bail!(
"no dotfiles matched target filter: {}",
self.targets.join(", ")
);
}
if files.is_empty() && edits.is_empty() {
super::warn_if_dotfiles_ignored();
info!("no dotfiles configured in [dotfiles]");
return Ok(());
}
let mut edit_opts = system::edits::UnapplyOpts {
dry_run: self.dry_run,
verbose: Settings::get().verbose,
force: self.force,
yes: self.yes,
};
let mut file_opts = system::files::UnapplyOpts {
dry_run: self.dry_run,
verbose: Settings::get().verbose,
force: self.force,
yes: self.yes,
};
let edit_plan = system::edits::plan_unapply(&edits, &edit_opts)?;
let mut file_plan = system::files::plan_unapply(&files, &file_opts)?;
if !self.dry_run
&& !self.yes
&& console::user_attended_stderr()
&& (!edit_plan.is_empty() || !file_plan.is_empty())
&& !prompt::confirm(format!(
"dotfiles: unapply {} file(s) and {} edit(s)?",
file_plan.len(),
edit_plan.len()
))?
.is_yes()
{
info!("dotfiles: skipped");
return Ok(());
}
system::files::resolve_unapply(&config, &mut file_plan, &file_opts)?;
system::edits::validate_unapply(&edit_plan)?;
edit_opts.yes = true;
file_opts.yes = true;
if !edits.is_empty() {
system::edits::execute_unapply(&edit_plan, &edit_opts)?;
}
if !files.is_empty() {
system::files::execute_unapply(&file_plan, &file_opts)?;
}
Ok(())
}
}