proto_cli 0.59.0

A multi-language version manager, a unified toolchain.
use crate::session::{ProtoSession, SessionResult};
use clap::Args;
use proto_core::{Id, PinLocation, PluginLocator, PluginType, ProtoConfig, cfg};
use starbase_console::ui::*;
use tracing::instrument;

#[derive(Args, Clone, Debug)]
pub struct PluginAddArgs {
    #[arg(required = true, help = "ID of plugin")]
    id: Id,

    #[arg(required = true, help = "Locator string to find and load the plugin")]
    plugin: PluginLocator,

    #[arg(long, default_value_t, help = "Location of .prototools to add to")]
    to: PinLocation,

    #[arg(long = "type", default_value_t, help = "The type of plugin to add")]
    ty: PluginType,
}

#[instrument(skip(session))]
pub async fn add(session: ProtoSession, args: PluginAddArgs) -> SessionResult {
    let config_path = ProtoConfig::update_document(session.env.get_config_dir(args.to), |doc| {
        let key = if args.ty == PluginType::Backend {
            "backends"
        } else {
            "tools"
        };

        // Convert legacy [plugins] to [plugins.tools]
        if doc.contains_key("plugins")
            && doc["plugins"].as_table().is_some_and(|table| {
                !table.contains_key("backends") && !table.contains_key("tools")
            })
        {
            let existing = doc["plugins"].clone();

            doc.remove("plugins");

            let plugins = doc["plugins"].or_insert(cfg::implicit_table());
            plugins["tools"] = existing;
        }

        // Add plugin to nested tables
        let plugins = doc["plugins"].or_insert(cfg::implicit_table());
        let table = plugins[key].or_insert(cfg::table());
        table[args.id.as_str()] = cfg::value(args.plugin.to_string());
    })?;

    // Load the tool and verify it works. We can't load the tool with the
    // session as the config has already been cached, and doesn't reflect
    // the recent addition!
    #[cfg(not(debug_assertions))]
    {
        use proto_core::ToolContext;
        use proto_core::reporter::NoticeOutput;

        let tool = proto_core::load_tool_from_locator(
            ToolContext::parse(&args.id)?,
            &session.env,
            &args.plugin,
        )
        .await?;

        if !tool.metadata.deprecations.is_empty() {
            session.console.notice_with(NoticeOutput {
                variant: Variant::Info,
                title: Some("Deprecations".into()),
                items: tool.metadata.deprecations,
                ..Default::default()
            })?;
        }
    }

    session.console.notice(
        Variant::Success,
        format!(
            "Added <id>{}</id> plugin to config <path>{}</path>",
            args.id,
            config_path.display(),
        ),
    )?;

    Ok(None)
}