void-cli 0.0.2

CLI for void — anonymous encrypted source control
//! Shared IPFS-related CLI helpers used by fork, pull-request, and publish.

use void_core::crypto::ContentKey;
use void_core::store::IpfsBackend;

use crate::observer::ProgressObserver;
use crate::output::CliError;

/// Parse a `--content-key` hex string into a `ContentKey`.
pub fn parse_content_key(content_key: &Option<String>) -> Result<ContentKey, CliError> {
    let hex_str = content_key.as_ref().ok_or_else(|| {
        CliError::invalid_args("--content-key is required")
    })?;
    ContentKey::from_hex(hex_str)
        .map_err(|e| CliError::invalid_args(format!("invalid content-key: {e}")))
}

/// Parse a required `--content-key` hex string into a `ContentKey`.
pub fn parse_content_key_required(hex_str: &str) -> Result<ContentKey, CliError> {
    ContentKey::from_hex(hex_str)
        .map_err(|e| CliError::invalid_args(format!("invalid content-key: {e}")))
}

/// Parse backend configuration from CLI arguments.
pub fn parse_backend(
    backend_type: &str,
    kubo_url: &str,
    gateway_url: &Option<String>,
) -> Result<IpfsBackend, CliError> {
    match backend_type {
        "gateway" => {
            let url = gateway_url.as_ref().ok_or_else(|| {
                CliError::invalid_args("--gateway <url> is required when backend is gateway")
            })?;
            Ok(IpfsBackend::Gateway { base: url.clone() })
        }
        "kubo" => Ok(IpfsBackend::Kubo {
            api: kubo_url.to_string(),
        }),
        other => Err(CliError::invalid_args(format!(
            "invalid backend '{}': expected kubo or gateway",
            other
        ))),
    }
}

/// Create a progress observer (hidden in JSON mode).
pub fn make_observer(use_json: bool, message: &str) -> ProgressObserver {
    if use_json {
        ProgressObserver::new_hidden()
    } else {
        ProgressObserver::new(message)
    }
}

/// Format byte count as human-readable string.
pub fn format_bytes(bytes: u64) -> String {
    const KB: u64 = 1024;
    const MB: u64 = KB * 1024;
    const GB: u64 = MB * 1024;

    if bytes >= GB {
        format!("{:.1} GB", bytes as f64 / GB as f64)
    } else if bytes >= MB {
        format!("{:.1} MB", bytes as f64 / MB as f64)
    } else if bytes >= KB {
        format!("{:.1} KB", bytes as f64 / KB as f64)
    } else {
        format!("{} B", bytes)
    }
}