proto_cli 0.57.3

A multi-language version manager, a unified toolchain.
use crate::session::{LoadToolOptions, ProtoSession};
use clap::{Args, ValueEnum};
use proto_core::flow::locate::Locator;
use proto_core::flow::resolve::Resolver;
use proto_core::{ToolContext, ToolSpec};
use starbase::AppResult;

#[derive(Clone, Debug, ValueEnum)]
enum BinDirType {
    Exes,
    Globals,
}

#[derive(Args, Clone, Debug)]
pub struct BinArgs {
    #[arg(required = true, help = "Tool to inspect")]
    context: ToolContext,

    #[arg(long, help = "List all paths instead of just one")]
    all: bool,

    #[arg(
        value_enum,
        long,
        help = "Display the chosen directory path if available"
    )]
    dir: Option<BinDirType>,

    #[arg(long, help = "Display symlinked binary path when available")]
    bin: bool,

    #[arg(help = "Version specification to locate")]
    spec: Option<ToolSpec>,

    #[arg(long, help = "Display shim path when available")]
    shim: bool,
}

#[tracing::instrument(skip_all)]
pub async fn bin(session: ProtoSession, args: BinArgs) -> AppResult {
    let tool = session
        .load_tool_with_options(
            &args.context,
            LoadToolOptions {
                detect_version: args.spec.is_none(),
                ..Default::default()
            },
        )
        .await?;

    let mut spec = args
        .spec
        .clone()
        .or_else(|| tool.detected_version.clone())
        .unwrap_or_else(|| tool.spec.clone());

    Resolver::resolve(&tool, &mut spec, true).await?;

    let mut locator = Locator::new(&tool, &spec);

    if args.bin {
        for bin in locator.locate_bins(None).await? {
            if bin.config.primary {
                session
                    .console
                    .out
                    .write_line(bin.path.display().to_string())?;

                return Ok(None);
            }
        }
    }

    if args.shim {
        for shim in locator.locate_shims().await? {
            if shim.config.primary {
                session
                    .console
                    .out
                    .write_line(shim.path.display().to_string())?;

                return Ok(None);
            }
        }
    }

    let paths = match args.dir {
        None => vec![locator.locate_exe_file().await?],
        Some(BinDirType::Exes) => locator.locate_exes_dirs().await?,
        Some(BinDirType::Globals) => locator.locate_globals_dirs().await?,
    };

    if args.all {
        for path in paths {
            session.console.out.write_line(path.display().to_string())?;
        }
    } else if let Some(path) = paths.first() {
        session.console.out.write_line(path.display().to_string())?;
    }

    Ok(None)
}