forklaunch 1.18.0

Launch faster with forklaunch
use std::io::Write;

use anyhow::{Context, Result};
use clap::{Arg, ArgAction, ArgMatches, Command};
use termcolor::{ColorChoice, StandardStream, WriteColor};

use crate::{
    CliCommand,
    core::command::command,
    managed::client::{
        Missing, delete_text, print_dryrun, require_managed_mode, resolve_managed_auth,
    },
};

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

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

impl CliCommand for UnsetCommand {
    fn command(&self) -> Command {
        command("unset", "Clear one instance's value for a custom variable")
            .long_about(
                "Clear one instance's value for a custom variable.\n\n\
                 The template's DECLARATION stays. Only this instance's value goes away, so\n\
                 the variable still appears in `vars list` — as MISSING. If it was declared\n\
                 --required, clearing it means this instance can no longer be provisioned\n\
                 until a new value is supplied.\n\n\
                 To remove the variable everywhere, remove the declaration instead:\n\
                 `forklaunch managed template vars unset --slug <slug> --key <key>`.\n\n\
                 There is no --scope or --service, for the same reason `set` has none: the\n\
                 template's declaration fixed the scoping, and an instance holds one value per\n\
                 key.",
            )
            .arg(
                Arg::new("id")
                    .long("id")
                    .required(true)
                    .help("Id of the instance whose value should be cleared"),
            )
            .arg(
                Arg::new("key")
                    .long("key")
                    .required(true)
                    .help("Name of the custom variable to clear"),
            )
            .arg(
                Arg::new("dryrun")
                    .long("dryrun")
                    .help("Print the request that would be sent without sending it")
                    .action(ArgAction::SetTrue),
            )
    }

    fn handler(&self, matches: &ArgMatches) -> Result<()> {
        let mut stdout = StandardStream::stdout(ColorChoice::Always);

        let id = matches
            .get_one::<String>("id")
            .context("--id is required")?;
        let key = matches
            .get_one::<String>("key")
            .context("--key is required")?;

        let path = format!(
            "/instances/{}/variables/{}",
            urlencoding::encode(id),
            urlencoding::encode(key)
        );

        if matches.get_flag("dryrun") {
            return print_dryrun("DELETE", &path, None);
        }

        let auth_mode = resolve_managed_auth()?;
        require_managed_mode(&auth_mode)?;

        delete_text(
            &path,
            Missing::Resource(format!("value for '{}' on instance '{}'", key, id)),
        )?;

        log_ok!(stdout, "Cleared '{}' for instance {}", key, id);
        log_info!(
            stdout,
            "The template still declares it, so it now shows as MISSING. If it is required, this instance cannot be provisioned until a new value is set."
        );

        Ok(())
    }
}