tovuk 0.1.101

Deploy Rust workers, static frontends, and full-stack services to Tovuk.
use super::super::{
    args::CliOptions,
    errors::{Result, agent_error},
    project::encode_component,
};
use super::{
    common::{command_arg, service_route},
    generic::{print_authenticated_mutation, service_get_command},
};
use reqwest::Method;
use serde_json::json;

#[derive(Clone, Copy)]
enum EnvCommandSurface {
    Env,
    Secrets,
}

pub(crate) fn env_command(cli: &CliOptions) -> Result<()> {
    scoped_env_command(cli, EnvCommandSurface::Env)
}

pub(crate) fn secrets_command(cli: &CliOptions) -> Result<()> {
    scoped_env_command(cli, EnvCommandSurface::Secrets)
}

fn scoped_env_command(cli: &CliOptions, surface: EnvCommandSurface) -> Result<()> {
    match cli.args.first().map_or("list", String::as_str) {
        "list" => service_get_command(cli, "env"),
        "set" | "put" => env_set(cli, surface),
        "delete" => env_delete(cli, surface),
        _ => Err(agent_error(
            "unknown_command",
            format!("Unknown {} command.", surface.name()),
            surface.command_help(),
            cli.output.json,
        )),
    }
}

fn env_set(cli: &CliOptions, surface: EnvCommandSurface) -> Result<()> {
    let assignment = cli.args.get(1).cloned().unwrap_or_default();
    let separator = assignment.find('=').unwrap_or(0);
    if separator == 0 {
        return Err(agent_error(
            "invalid_env",
            format!("{} assignment must be KEY=value.", surface.item_name()),
            format!(
                "Pass one uppercase shell-safe {} assignment, for example `API_KEY=value`.",
                surface.item_name().to_lowercase()
            ),
            cli.output.json,
        ));
    }
    let name = &assignment[..separator];
    let value = &assignment[separator + 1..];
    print_authenticated_mutation(
        cli,
        Method::PUT,
        &service_route(cli, "env")?,
        Some(json!({ "name": name, "value": value })),
    )
}

fn env_delete(cli: &CliOptions, surface: EnvCommandSurface) -> Result<()> {
    let message = format!("{} name is required.", surface.item_name());
    let instruction = format!("Use `{}`.", surface.delete_example());
    let name = command_arg(cli, "invalid_env", &message, &instruction)?;
    print_authenticated_mutation(
        cli,
        Method::DELETE,
        &service_route(cli, &format!("env/{}", encode_component(&name)))?,
        None,
    )
}

impl EnvCommandSurface {
    fn command_help(self) -> &'static str {
        match self {
            Self::Env => "Use `tovuk env list`, `env set`, or `env delete`.",
            Self::Secrets => {
                "Use `tovuk secrets list`, `secrets set`, `secrets put`, or `secrets delete`."
            }
        }
    }

    fn delete_example(self) -> &'static str {
        match self {
            Self::Env => "tovuk env delete --service <service> KEY",
            Self::Secrets => "tovuk secrets delete --service <service> KEY",
        }
    }

    fn item_name(self) -> &'static str {
        match self {
            Self::Env => "Environment variable",
            Self::Secrets => "Secret",
        }
    }

    fn name(self) -> &'static str {
        match self {
            Self::Env => "env",
            Self::Secrets => "secrets",
        }
    }
}