runmat 0.4.5

High-performance MATLAB/Octave syntax mathematical runtime
Documentation
use anyhow::{Context, Result};
use chrono::{DateTime, Utc};
use serde::Deserialize;
use uuid::Uuid;

use crate::remote::shared::build_http_client;

#[derive(Deserialize)]
struct SnapshotListResponse {
    items: Vec<SnapshotResponse>,
}

#[derive(Deserialize)]
struct SnapshotResponse {
    id: Uuid,
    #[serde(rename = "projectId")]
    _project_id: Uuid,
    #[serde(rename = "parentId")]
    parent_id: Option<Uuid>,
    message: Option<String>,
    #[serde(rename = "createdAt")]
    created_at: DateTime<Utc>,
    #[serde(rename = "createdBy")]
    created_by: Option<Uuid>,
    tags: Vec<String>,
}

#[derive(Deserialize)]
struct SnapshotTagListResponse {
    items: Vec<SnapshotTagResponse>,
}

#[derive(Deserialize)]
struct SnapshotTagResponse {
    tag: String,
    #[serde(rename = "snapshotId")]
    snapshot_id: Uuid,
    #[serde(rename = "createdAt")]
    created_at: DateTime<Utc>,
    #[serde(rename = "createdBy")]
    created_by: Option<Uuid>,
}

pub async fn list_snapshots(server_url: &str, token: &str, project_id: Uuid) -> Result<()> {
    let (client, base) = build_http_client(server_url, token)?;
    let url = format!("{}/v1/projects/{}/fs/snapshots", base, project_id);
    let response = client
        .get(&url)
        .send()
        .await
        .context("Failed to fetch snapshots")?;
    if !response.status().is_success() {
        let status = response.status();
        let text = response.text().await.unwrap_or_default();
        anyhow::bail!("Snapshot list failed ({status}): {text}")
    }
    let payload = response
        .json::<SnapshotListResponse>()
        .await
        .context("Failed to parse snapshot list")?;
    for item in payload.items {
        let parent = item
            .parent_id
            .map(|value| value.to_string())
            .unwrap_or_else(|| "-".to_string());
        let message = item.message.unwrap_or_default();
        let tags = if item.tags.is_empty() {
            "-".to_string()
        } else {
            item.tags.join(",")
        };
        let created_by = item
            .created_by
            .map(|value| value.to_string())
            .unwrap_or_else(|| "-".to_string());
        println!(
            "{}\t{}\t{}\t{}\t{}\t{}",
            item.id, item.created_at, parent, created_by, tags, message
        );
    }
    Ok(())
}

pub async fn create_snapshot(
    server_url: &str,
    token: &str,
    project_id: Uuid,
    message: Option<String>,
    parent: Option<Uuid>,
    tag: Option<String>,
) -> Result<()> {
    let (client, base) = build_http_client(server_url, token)?;
    let url = format!("{}/v1/projects/{}/fs/snapshots", base, project_id);
    let response = client
        .post(&url)
        .json(&serde_json::json!({
            "message": message,
            "parentId": parent,
            "tag": tag,
        }))
        .send()
        .await
        .context("Failed to create snapshot")?;
    if !response.status().is_success() {
        let status = response.status();
        let text = response.text().await.unwrap_or_default();
        anyhow::bail!("Snapshot create failed ({status}): {text}")
    }
    let payload = response
        .json::<SnapshotResponse>()
        .await
        .context("Failed to parse snapshot response")?;
    let parent = payload
        .parent_id
        .map(|value| value.to_string())
        .unwrap_or_else(|| "-".to_string());
    let message = payload.message.unwrap_or_default();
    let tags = if payload.tags.is_empty() {
        "-".to_string()
    } else {
        payload.tags.join(",")
    };
    let created_by = payload
        .created_by
        .map(|value| value.to_string())
        .unwrap_or_else(|| "-".to_string());
    println!(
        "{}\t{}\t{}\t{}\t{}\t{}",
        payload.id, payload.created_at, parent, created_by, tags, message
    );
    Ok(())
}

pub async fn restore_snapshot(
    server_url: &str,
    token: &str,
    project_id: Uuid,
    snapshot_id: Uuid,
) -> Result<()> {
    let (client, base) = build_http_client(server_url, token)?;
    let url = format!(
        "{}/v1/projects/{}/fs/snapshots/{}/restore",
        base, project_id, snapshot_id
    );
    let response = client
        .post(&url)
        .send()
        .await
        .context("Failed to restore snapshot")?;
    if !response.status().is_success() {
        let status = response.status();
        let text = response.text().await.unwrap_or_default();
        anyhow::bail!("Snapshot restore failed ({status}): {text}")
    }
    println!("Restored snapshot {snapshot_id}");
    Ok(())
}

pub async fn delete_snapshot(
    server_url: &str,
    token: &str,
    project_id: Uuid,
    snapshot_id: Uuid,
) -> Result<()> {
    let (client, base) = build_http_client(server_url, token)?;
    let url = format!(
        "{}/v1/projects/{}/fs/snapshots/{}",
        base, project_id, snapshot_id
    );
    let response = client
        .delete(&url)
        .send()
        .await
        .context("Failed to delete snapshot")?;
    if !response.status().is_success() {
        let status = response.status();
        let text = response.text().await.unwrap_or_default();
        anyhow::bail!("Snapshot delete failed ({status}): {text}")
    }
    println!("Deleted snapshot {snapshot_id}");
    Ok(())
}

pub async fn list_snapshot_tags(server_url: &str, token: &str, project_id: Uuid) -> Result<()> {
    let (client, base) = build_http_client(server_url, token)?;
    let url = format!("{}/v1/projects/{}/fs/snapshots/tags", base, project_id);
    let response = client
        .get(&url)
        .send()
        .await
        .context("Failed to fetch snapshot tags")?;
    if !response.status().is_success() {
        let status = response.status();
        let text = response.text().await.unwrap_or_default();
        anyhow::bail!("Snapshot tag list failed ({status}): {text}")
    }
    let payload = response
        .json::<SnapshotTagListResponse>()
        .await
        .context("Failed to parse snapshot tags")?;
    for item in payload.items {
        let created_by = item
            .created_by
            .map(|value| value.to_string())
            .unwrap_or_else(|| "-".to_string());
        println!(
            "{}\t{}\t{}\t{}",
            item.tag, item.snapshot_id, item.created_at, created_by
        );
    }
    Ok(())
}

pub async fn set_snapshot_tag(
    server_url: &str,
    token: &str,
    project_id: Uuid,
    snapshot_id: Uuid,
    tag: &str,
) -> Result<()> {
    let (client, base) = build_http_client(server_url, token)?;
    let url = format!(
        "{}/v1/projects/{}/fs/snapshots/{}/tags",
        base, project_id, snapshot_id
    );
    let response = client
        .post(&url)
        .json(&serde_json::json!({ "tag": tag }))
        .send()
        .await
        .context("Failed to set snapshot tag")?;
    if !response.status().is_success() {
        let status = response.status();
        let text = response.text().await.unwrap_or_default();
        anyhow::bail!("Snapshot tag set failed ({status}): {text}")
    }
    let payload = response
        .json::<SnapshotTagResponse>()
        .await
        .context("Failed to parse snapshot tag")?;
    let created_by = payload
        .created_by
        .map(|value| value.to_string())
        .unwrap_or_else(|| "-".to_string());
    println!(
        "{}\t{}\t{}\t{}",
        payload.tag, payload.snapshot_id, payload.created_at, created_by
    );
    Ok(())
}

pub async fn delete_snapshot_tag(
    server_url: &str,
    token: &str,
    project_id: Uuid,
    tag: &str,
) -> Result<()> {
    let (client, base) = build_http_client(server_url, token)?;
    let url = format!(
        "{}/v1/projects/{}/fs/snapshots/tags/{}",
        base, project_id, tag
    );
    let response = client
        .delete(&url)
        .send()
        .await
        .context("Failed to delete snapshot tag")?;
    if !response.status().is_success() {
        let status = response.status();
        let text = response.text().await.unwrap_or_default();
        anyhow::bail!("Snapshot tag delete failed ({status}): {text}")
    }
    println!("Deleted snapshot tag {tag}");
    Ok(())
}