mise 2026.9.4

Dev tools, env vars, and tasks in one CLI
use console::style;
use eyre::{Result, bail};

use crate::backend::Backend;
use crate::cli::args::BackendArg;
use crate::config::Config;
use crate::toolset::{Toolset, ToolsetBuilder};

/// Shows current active and installed runtime versions
///
/// This is similar to `mise ls --current`, but this only shows the runtime
/// and/or version. It's designed to fit into scripts more easily.
#[derive(Debug, usage_rs::Args)]
#[usage(
    verbatim_doc_comment,
    hide = true,
    example(
        r###"mise current
python 3.11.0 3.10.0
shfmt 3.6.0
shellcheck 0.9.0
node 20.0.0"###,
        help = r###"outputs `.tool-versions` compatible format"###
    ),
    example(
        r###"mise current node
20.0.0"###
    ),
    example(
        r###"mise current python
3.11.0 3.10.0"###,
        help = r###"can output multiple versions"###
    )
)]
pub(crate) struct Current {
    /// Plugin to show versions of
    /// e.g.: ruby, node, cargo:eza, npm:prettier, etc.
    #[usage()]
    plugin: Option<BackendArg>,
}

impl Current {
    pub(crate) async fn run(self) -> Result<()> {
        let config = Config::get().await?;
        let ts = ToolsetBuilder::new().build(&config).await?;
        match &self.plugin {
            Some(ba) => {
                if let Some(plugin) = ba.backend()?.plugin()
                    && !plugin.is_installed()
                {
                    bail!("Plugin {ba} is not installed");
                }
                self.one(ts, ba.backend()?.as_ref()).await
            }
            None => self.all(ts).await,
        }
    }

    async fn one(&self, ts: Toolset, tool: &dyn Backend) -> Result<()> {
        if let Some(plugin) = tool.plugin()
            && !plugin.is_installed()
        {
            warn!("Plugin {} is not installed", tool.id());
            return Ok(());
        }
        match ts
            .list_versions_by_plugin()
            .into_iter()
            .find(|(p, _)| p.id() == tool.id())
        {
            Some((_, tvl)) => {
                let versions = tvl.os_supported_versions().collect::<Vec<_>>();
                if versions.is_empty() {
                    warn!(
                        "Plugin {} does not have a version set",
                        style(tool.id()).blue().for_stderr()
                    );
                    return Ok(());
                }
                miseprintln!(
                    "{}",
                    versions
                        .iter()
                        .map(|v| v.version.to_string())
                        .collect::<Vec<_>>()
                        .join(" ")
                );
            }
            None => {
                warn!(
                    "Plugin {} does not have a version set",
                    style(tool.id()).blue().for_stderr()
                );
            }
        };
        Ok(())
    }

    async fn all(&self, ts: Toolset) -> Result<()> {
        let config = Config::get().await?;
        for (plugin, tvl) in ts.list_versions_by_plugin() {
            let versions = tvl.os_supported_versions().collect::<Vec<_>>();
            if versions.is_empty() {
                continue;
            }
            for tv in &versions {
                if !plugin.is_version_installed(&config, tv, true) {
                    let source = ts.versions.get(tv.ba()).unwrap().source.clone();
                    warn!(
                        "{}@{} is specified in {}, but not installed",
                        &tv.ba(),
                        &tv.version,
                        &source
                    );
                    hint!(
                        "tools_missing",
                        "install missing tools with",
                        "mise install"
                    );
                }
            }
            miseprintln!(
                "{} {}",
                &plugin.id(),
                versions
                    .iter()
                    .map(|v| v.version.to_string())
                    .collect::<Vec<_>>()
                    .join(" ")
            );
        }
        Ok(())
    }
}