void-cli 0.0.4

CLI for void — anonymous encrypted source control
//! Unified backend selection for all network commands.
//!
//! Provides a single `resolve_backend()` function that all network commands
//! (push, pull, clone, fork, pull-request) use to select and initialize
//! the transport backend.

use std::path::Path;
use std::sync::Arc;
use std::time::Duration;

use void_core::store::{IpfsBackend, IpfsStore, RemoteStore};

use crate::output::CliError;

/// Resolved backend — either a daemon-backed remote store or an IpfsStore.
pub struct ResolvedBackend {
    /// The IpfsBackend enum (used by core options structs that still need it).
    pub ipfs_backend: IpfsBackend,
    /// The remote store (daemon or IpfsStore). Use this for operations.
    pub remote: Arc<dyn RemoteStore>,
    /// Tokio runtime kept alive for daemon mode (dropped = daemon stops).
    pub _runtime: Option<tokio::runtime::Runtime>,
}

/// Resolve the network backend from CLI arguments.
///
/// All network commands should call this instead of parsing backends locally.
///
/// - `"daemon"` (default): starts or connects to an embedded void daemon.
///   No external dependencies. Uses bitswap + DHT.
/// - `"kubo"`: connects to a local Kubo node via HTTP API.
/// - `"gateway"`: fetches from a public IPFS gateway (read-only).
pub fn resolve_backend(
    backend_type: Option<&str>,
    kubo_url: &str,
    gateway_url: &Option<String>,
    void_dir: Option<&Path>,
    timeout: Duration,
) -> Result<ResolvedBackend, CliError> {
    match backend_type.unwrap_or("daemon") {
        "daemon" => {
            let dir = void_dir.ok_or_else(|| {
                CliError::internal("daemon backend requires a void directory")
            })?;
            let (remote, rt) = crate::daemon::start_daemon(dir)
                .map_err(|e| CliError::internal(e))?;
            Ok(ResolvedBackend {
                ipfs_backend: IpfsBackend::Kubo { api: "daemon".to_string() },
                remote,
                _runtime: Some(rt),
            })
        }
        "kubo" => {
            let backend = IpfsBackend::Kubo { api: kubo_url.to_string() };
            let store = Arc::new(IpfsStore::new(backend.clone(), timeout));
            Ok(ResolvedBackend {
                ipfs_backend: backend,
                remote: store,
                _runtime: None,
            })
        }
        "gateway" => {
            let url = gateway_url.as_ref().ok_or_else(|| {
                CliError::invalid_args("--gateway <url> is required when backend is gateway")
            })?;
            let backend = IpfsBackend::Gateway { base: url.clone() };
            let store = Arc::new(IpfsStore::new(backend.clone(), timeout));
            Ok(ResolvedBackend {
                ipfs_backend: backend,
                remote: store,
                _runtime: None,
            })
        }
        other => Err(CliError::invalid_args(format!(
            "invalid backend '{other}': expected daemon, kubo, or gateway"
        ))),
    }
}