mise 2026.4.11

The front-end to your dev env
use std::path::Path;

use eyre::Result;

use crate::config::Settings;
use crate::file::remove_all;
use crate::ui::prompt;
use crate::{dirs, env, file};
use std::collections::BTreeSet;

/// Removes mise CLI and all related data
///
/// Skips config directory by default.
#[derive(Debug, clap::Args)]
#[clap(verbatim_doc_comment)]
pub struct Implode {
    /// List directories that would be removed without actually removing them
    #[clap(long, short = 'n', verbatim_doc_comment)]
    dry_run: bool,

    /// Also remove config directory
    #[clap(long, verbatim_doc_comment)]
    config: bool,
}

impl Implode {
    pub fn run(self) -> Result<()> {
        let mut files: BTreeSet<&Path> = [*dirs::STATE, *dirs::DATA, *dirs::CACHE, &*env::MISE_BIN]
            .into_iter()
            .collect();
        if self.config {
            files.insert(&dirs::CONFIG);
        }
        // include system data dir (e.g. /usr/local/share/mise) used by `mise install --system`
        let system_data: &Path = &env::MISE_SYSTEM_DATA_DIR;
        files.insert(system_data);
        for f in files.into_iter().filter(|d| d.exists()) {
            if self.dry_run {
                miseprintln!("rm -rf {}", f.display());
            }

            if self.confirm_remove(f)? {
                if f.is_dir() {
                    remove_all(f)?;
                } else {
                    file::remove_file(f)?;
                }
            }
        }

        Ok(())
    }

    fn confirm_remove(&self, f: &Path) -> Result<bool> {
        let settings = Settings::try_get()?;
        if self.dry_run {
            Ok(false)
        } else if settings.yes {
            Ok(true)
        } else {
            let r = prompt::confirm(format!("remove {} ?", f.display()))?;
            Ok(r)
        }
    }
}