mise 2026.9.10

Dev tools, env vars, and tasks in one CLI
use std::ffi::OsStr;
use std::sync::Arc;

use eyre::Result;
use itertools::Itertools;

use crate::cli::Cli;
use crate::cli::install::Install;
use crate::cli::ls_remote::LsRemote;
use crate::cli::reshim::Reshim;
use crate::config::{Config, config_file};
use crate::toolset::ToolsetBuilder;

/// [internal] simulates asdf for plugins that call "asdf" internally
#[derive(Debug, usage_rs::Args)]
#[usage(hide = true, verbatim_doc_comment)]
pub(crate) struct Asdf {
    /// all arguments
    #[usage(allow_hyphen_values = true, value_hint = usage_rs::ValueHint::CommandWithArguments, trailing_var_arg = true)]
    args: Vec<String>,
}

impl Asdf {
    pub(crate) async fn run(mut self) -> Result<()> {
        let config = Config::get().await?;
        let mut args = vec![String::from("mise")];
        args.append(&mut self.args);

        match args.get(1).map(|s| s.as_str()) {
            Some("reshim") => {
                let command = parse_forwarded_args::<Reshim>(&args)?;
                prepare_forwarded_command(&args, false)?;
                command.run().await
            }
            Some("list") => list_versions(&config, &args).await,
            Some("install") => {
                if args.len() == 4 {
                    let version = args.pop().unwrap();
                    args[2] = format!("{}@{}", args[2], version);
                }
                let command = parse_forwarded_args::<Install>(&args)?;
                prepare_forwarded_command(&args, true)?;
                Box::pin(command.run()).await
            }
            _ => Box::pin(Cli::run(&args)).await,
        }
    }
}

fn prepare_forwarded_command(args: &[String], implicitly_trusts_active_config: bool) -> Result<()> {
    {
        let mut global_args = crate::env::ARGS.write().unwrap();
        global_args.clear();
        global_args.extend_from_slice(args);
    }
    config_file::set_implicitly_trust_active_config(implicitly_trusts_active_config);
    config_file::trust_active_config()
}

fn parse_forwarded_args<T: usage_rs::spec::CommandArgs>(args: &[String]) -> Result<T> {
    let argv = args[1..]
        .iter()
        .map(|arg| OsStr::new(arg.as_str()))
        .collect_vec();
    usage_rs::parse_args_from_argv(&argv).map_err(|err| super::usage_error(&argv, err))
}

async fn list_versions(config: &Arc<Config>, args: &[String]) -> Result<()> {
    if args[2] == "all" {
        return LsRemote {
            prefix: None,
            all: false,
            plugin: args.get(3).map(|s| s.parse()).transpose()?,
            json: false,
            prerelease: false,
            no_versions_host: false,
            strict_metadata: false,
            minimum_release_age: None,
        }
        .run()
        .await;
    }
    let ts = ToolsetBuilder::new().build(config).await?;
    let mut versions = ts.list_installed_versions(config).await?;
    let plugin = match args.len() {
        3 => Some(&args[2]),
        _ => None,
    };
    if let Some(plugin) = plugin {
        versions.retain(|(_, v)| &v.ba().to_string() == plugin);
        for (_, version) in versions {
            miseprintln!("{}", version.version);
        }
    } else {
        for (plugin, versions) in &versions.into_iter().chunk_by(|(_, v)| v.ba().clone()) {
            miseprintln!("{}", plugin);
            for (_, tv) in versions {
                miseprintln!("  {}", tv.version);
            }
        }
    }

    Ok(())
}