tovuk 0.1.67

Deploy Rust workers, static frontends, and worker-static apps to Tovuk.
use std::{
    fs::{self, File},
    path::Path,
};

use reqwest::{
    blocking::{Body, Client},
    header::{CONTENT_LENGTH, CONTENT_TYPE},
};
use serde_json::Value;

use crate::cli::{
    args::CliOptions,
    errors::{Result, agent_error, internal_error},
    project::string_field,
};

pub(super) fn upload_presigned_file(
    cli: &CliOptions,
    upload: &Value,
    local_path: &Path,
    size_bytes: u64,
    content_type: &str,
) -> Result<()> {
    let url = string_field(upload, "url");
    if url.is_empty() {
        return Err(internal_error(
            "Tovuk API did not return a storage upload URL.",
        ));
    }
    let file = File::open(local_path).map_err(|error| {
        agent_error(
            "storage_file_unreadable",
            format!("Could not open local storage file: {error}"),
            "Pass a readable file path and retry the upload.",
            cli.output.json,
        )
    })?;
    let response = Client::new()
        .put(url)
        .header(CONTENT_TYPE, content_type)
        .header(CONTENT_LENGTH, size_bytes)
        .body(Body::new(file))
        .send()
        .map_err(|error| {
            agent_error(
                "storage_upload_failed",
                format!("Storage upload failed: {error}"),
                "Retry the upload. If it keeps failing, create a support ticket with the service id and storage path.",
                cli.output.json,
            )
        })?;
    if response.status().is_success() {
        return Ok(());
    }
    Err(agent_error(
        "storage_upload_failed",
        format!(
            "Storage upload returned HTTP {}.",
            response.status().as_u16()
        ),
        "Retry the upload. If it keeps failing, create a support ticket with the service id and storage path.",
        cli.output.json,
    ))
}

pub(super) fn download_presigned_file(
    cli: &CliOptions,
    download: &Value,
    destination: &Path,
) -> Result<u64> {
    let url = string_field(download, "url");
    if url.is_empty() {
        return Err(internal_error(
            "Tovuk API did not return a storage download URL.",
        ));
    }
    if let Some(parent) = destination
        .parent()
        .filter(|parent| !parent.as_os_str().is_empty())
    {
        fs::create_dir_all(parent).map_err(|error| {
            agent_error(
                "storage_download_failed",
                format!("Could not create storage download directory: {error}"),
                "Choose a writable destination path and retry the download.",
                cli.output.json,
            )
        })?;
    }
    let mut response = Client::new().get(url).send().map_err(|error| {
        agent_error(
            "storage_download_failed",
            format!("Storage download failed: {error}"),
            "Retry the download. If it keeps failing, create a support ticket with the service id and storage path.",
            cli.output.json,
        )
    })?;
    if !response.status().is_success() {
        return Err(agent_error(
            "storage_download_failed",
            format!(
                "Storage download returned HTTP {}.",
                response.status().as_u16()
            ),
            "Retry the download. If it keeps failing, create a support ticket with the service id and storage path.",
            cli.output.json,
        ));
    }
    let mut output = File::create(destination).map_err(|error| {
        agent_error(
            "storage_download_failed",
            format!("Could not create storage download file: {error}"),
            "Choose a writable destination path and retry the download.",
            cli.output.json,
        )
    })?;
    response.copy_to(&mut output).map_err(|error| {
        agent_error(
            "storage_download_failed",
            format!("Could not write storage download file: {error}"),
            "Choose a writable destination path and retry the download.",
            cli.output.json,
        )
    })
}