vflight 0.9.2

Share files over the Veilid distributed network with content-addressable storage
Documentation
use anyhow::{Context, Result};
use std::sync::Arc;
use tokio::sync::mpsc;
use tracing::{debug, error, instrument};
use veilid_core::{api_startup, VeilidAPI, VeilidConfig, VeilidUpdate};

#[instrument(level = "info")]
pub async fn start_node(
    namespace: &str,
) -> Result<(VeilidAPI, mpsc::UnboundedReceiver<VeilidUpdate>)> {
    let (tx, rx) = mpsc::unbounded_channel();

    let tx_clone = tx.clone();
    let update_callback = Arc::new(move |update: VeilidUpdate| {
        let _ = tx_clone.send(update);
    });

    // VeilidConfig::new(program_name, organization, qualifier, storage_directory, config_directory)
    let mut config = VeilidConfig::new("vflight", "veilid", "", None, None);

    // Set namespace to allow multiple instances
    config.namespace = namespace.to_string();

    // Fall back to insecure storage only if secure keyring is unavailable (e.g., WSL)
    config.protected_store.allow_insecure_fallback = true;
    config.protected_store.always_use_insecure_storage = false;

    let api = api_startup(update_callback, config)
        .await
        .context("Failed to start Veilid API")?;
    debug!("Veilid API started successfully");

    api.attach().await.context("Failed to attach to network")?;
    debug!("Network attach initiated");

    Ok((api, rx))
}

#[instrument(level = "info", skip(api))]
pub async fn stop_node(api: VeilidAPI) -> Result<()> {
    api.detach()
        .await
        .context("Failed to detach from network")?;
    debug!("Detached from network");

    api.shutdown().await;
    debug!("Veilid API shutdown complete");
    Ok(())
}

#[instrument(level = "debug", skip(rx))]
pub async fn wait_for_attach(rx: &mut mpsc::UnboundedReceiver<VeilidUpdate>) -> Result<()> {
    use veilid_core::AttachmentState;

    loop {
        match rx.recv().await {
            Some(VeilidUpdate::Attachment(info)) => {
                tracing::info!("Attachment state: {:?}", info.state);
                if info.state == AttachmentState::AttachedGood
                    || info.state == AttachmentState::AttachedStrong
                    || info.state == AttachmentState::FullyAttached
                    || info.state == AttachmentState::OverAttached
                {
                    return Ok(());
                }
            }
            Some(_) => {}
            None => {
                error!("Update channel closed unexpectedly while waiting for attachment");
                anyhow::bail!("Update channel closed while waiting for attachment");
            }
        }
    }
}