mise 2026.9.2

Dev tools, env vars, and tasks in one CLI
use eyre::Result;

use crate::cli::args::ToolArg;
use crate::config::Config;
use crate::errors::Error;
use crate::toolset::ToolsetBuilder;

/// Display the installation path for a tool
///
/// The tool must be installed for this to work.
#[derive(Debug, usage_rs::Args)]
#[usage(
    verbatim_doc_comment,
    example(
        r###"mise where node@20
/home/jdx/.local/share/mise/installs/node/20.0.0"###,
        help = r###"Show the latest installed node 20.x Errors if no matching version is installed"###
    ),
    example(
        r###"mise where node
/home/jdx/.local/share/mise/installs/node/20.0.0"###,
        help = r###"Show the install directory of the active node, or of the latest installed version if no config requests node Errors if no matching version is installed"###
    )
)]
pub(crate) struct Where {
    /// Tool to look up
    /// e.g.: ruby@3
    /// With "@<PREFIX>", shows the latest installed version matching the prefix.
    /// Otherwise, shows the current, active installed version.
    #[usage(value_name = "TOOL@VERSION", verbatim_doc_comment)]
    tool: ToolArg,

    /// the version prefix to use when querying the latest version
    /// same as the first argument after the "@"
    /// used for asdf compatibility
    #[usage(hide = true, verbatim_doc_comment)]
    asdf_version: Option<String>,
}

impl Where {
    pub(crate) async fn run(self) -> Result<()> {
        let config = Config::get().await?;
        let tvr = match self.tool.tvr {
            Some(tvr) => tvr,
            None => match self.asdf_version {
                Some(version) => self.tool.with_version(&version).tvr.unwrap(),
                None => {
                    let ts = ToolsetBuilder::new().build(&config).await?;
                    match ts.versions.get(self.tool.ba.as_ref()) {
                        Some(tvl) => {
                            tvl.os_supported_requests().next().cloned().ok_or_else(|| {
                                eyre::eyre!("{} does not have an active version", self.tool.ba)
                            })?
                        }
                        None => self.tool.with_version("latest").tvr.unwrap(),
                    }
                }
            },
        };

        let tv = tvr.resolve(&config, &Default::default()).await?;

        if tv.backend()?.is_version_installed(&config, &tv, true) {
            miseprintln!("{}", tv.install_path().to_string_lossy());
            Ok(())
        } else {
            Err(Error::VersionNotInstalled(
                Box::new(tv.ba().clone()),
                tv.version,
            ))?
        }
    }
}