void-cli 0.0.4

CLI for void — anonymous encrypted source control
//! Export-car command — export a commit and its objects to a CAR file.
//!
//! The exported CAR can be transferred via SCP, sneakernet, etc.
//! and imported on a remote IPFS node with `ipfs dag import`.

use std::path::Path;

use serde::Serialize;
use void_core::pipeline::{export_commit_to_car, ExportCarOptions};

use crate::context::open_repo;
use crate::output::{run_command, CliError, CliOptions};

// ============================================================================
// Output types
// ============================================================================

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ExportCarOutput {
    pub commit_cid: String,
    pub car_path: String,
    pub blocks_exported: usize,
    pub car_size: u64,
}

// ============================================================================
// Args
// ============================================================================

pub struct ExportCarArgs {
    pub commit: Option<String>,
    pub output: Option<String>,
}

// ============================================================================
// Implementation
// ============================================================================

pub fn run(cwd: &Path, args: ExportCarArgs, opts: &CliOptions) -> Result<(), CliError> {
    run_command("export-car", opts, |ctx| {
        let repo = open_repo(cwd)?;

        let output_path = match args.output {
            Some(ref p) => std::path::PathBuf::from(p),
            None => {
                // Default: <cwd>/export.car
                cwd.join("export.car")
            }
        };

        let export_opts = ExportCarOptions {
            ctx: repo.context().clone(),
            commit_cid: args.commit.clone(),
        };

        let result = export_commit_to_car(export_opts, &output_path)
            .map_err(|e| CliError::internal(format!("failed to export CAR: {e}")))?;

        if !ctx.use_json() {
            ctx.info(format!("Exported commit {} to CAR", result.commit_cid));
            ctx.info(format!(
                "  Blocks: {}  Size: {} bytes",
                result.blocks_exported, result.car_size
            ));
            ctx.info(format!("  Path: {}", result.car_path.display()));
        }

        Ok(ExportCarOutput {
            commit_cid: result.commit_cid,
            car_path: result.car_path.to_string_lossy().to_string(),
            blocks_exported: result.blocks_exported,
            car_size: result.car_size,
        })
    })
}