forklaunch 1.15.0

Launch faster with forklaunch
use anyhow::{Context, Result, bail};
use clap::{Arg, ArgAction, ArgMatches, Command};
use dialoguer::{Input, theme::ColorfulTheme};

use crate::{
    CliCommand,
    core::{
        command::command,
        validate::{require_auth, require_integration, require_manifest},
    },
};

use super::{
    lifecycle::call_lifecycle_action,
    resource_resolver::{fetch_resource_detail, resolve},
};

#[derive(Debug)]
pub(super) struct DeleteCommand;

impl DeleteCommand {
    pub(super) fn new() -> Self {
        Self
    }
}

impl CliCommand for DeleteCommand {
    fn command(&self) -> Command {
        command(
            "delete",
            "Permanently delete a provisioned database, cache, or queue resource and its AWS infrastructure",
        )
        .arg(
            Arg::new("resource")
                .help("Resource identifier: <project-name>:<resource-type> (e.g. billing-service:database)")
                .required(true),
        )
        .arg(Arg::new("base_path").short('p').long("path").help("The application path"))
        .arg(
            Arg::new("environment")
                .short('e')
                .long("environment")
                .required(true)
                .help("Environment to target (for example: dev, staging, production)"),
        )
        .arg(
            Arg::new("resource_id")
                .long("resource-id")
                .help("Skip name resolution and address a resource by its platform id directly"),
        )
        .arg(
            Arg::new("yes")
                .long("yes")
                .short('y')
                .help("Skip the confirmation prompt (for CI/scripted use) — use with care, this is irreversible")
                .action(ArgAction::SetTrue),
        )
    }

    fn handler(&self, matches: &ArgMatches) -> Result<()> {
        require_auth()?;
        let (_app_root, manifest) = require_manifest(matches)?;
        let application_id = require_integration(&manifest)?;
        let environment = matches
            .get_one::<String>("environment")
            .expect("--environment is required")
            .to_string();
        let resource_arg = matches
            .get_one::<String>("resource")
            .expect("<resource> is required");
        let resource_id_override = matches.get_one::<String>("resource_id").map(String::as_str);
        let skip_confirm = matches.get_flag("yes");

        let resolved = resolve(
            &manifest,
            &application_id,
            &environment,
            resource_arg,
            resource_id_override,
        )?;

        let detail = fetch_resource_detail(&resolved.id)?;

        if !skip_confirm {
            println!(
                "This will PERMANENTLY DELETE {} ({}) and its AWS infrastructure.",
                detail.name, detail.r#type
            );
            println!("This action cannot be undone — no snapshot is taken automatically before deletion.");
            println!();

            let typed: String = Input::with_theme(&ColorfulTheme::default())
                .with_prompt(format!("Type the resource name ({}) to confirm", detail.name))
                .allow_empty(true)
                .interact_text()
                .with_context(|| "Failed to read confirmation")?;

            if typed != detail.name {
                bail!("confirmation did not match '{}' — aborted, nothing was deleted", detail.name);
            }
        }

        let result = call_lifecycle_action(&resolved.id, "delete")?;
        println!("{}", result.message);

        Ok(())
    }
}