mod common;
use cyber_bao::io::BaoContentItem;
use clap::Parser;
use common::setup_logging;
use iroh::address_lookup::PkarrResolver;
use iroh_blobs::{get::request::GetBlobItem, ticket::BlobTicket, BlobFormat};
use n0_future::StreamExt;
use tokio::io::AsyncWriteExt;
#[derive(Debug, Parser)]
#[command(version, about)]
pub struct Cli {
ticket: BlobTicket,
#[arg(long, default_value = "true")]
progress: bool,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
setup_logging();
let cli = Cli::parse();
let ticket = cli.ticket;
let endpoint = iroh::Endpoint::empty_builder(iroh::RelayMode::Default)
.address_lookup(PkarrResolver::n0_dns())
.bind()
.await?;
anyhow::ensure!(
ticket.format() == BlobFormat::Raw,
"This example only supports raw blobs."
);
let connection = endpoint.connect(ticket.addr().id, iroh_blobs::ALPN).await?;
let mut progress = iroh_blobs::get::request::get_blob(connection, ticket.hash());
let stats = if cli.progress {
loop {
match progress.next().await {
Some(GetBlobItem::Item(item)) => match item {
BaoContentItem::Leaf(leaf) => {
tokio::io::stdout().write_all(&leaf.data).await?;
}
BaoContentItem::Parent(parent) => {
tracing::info!("Parent: {parent:?}");
}
},
Some(GetBlobItem::Done(stats)) => {
break stats;
}
Some(GetBlobItem::Error(err)) => {
anyhow::bail!("Error while streaming blob: {err}");
}
None => {
anyhow::bail!("Stream ended unexpectedly.");
}
}
}
} else {
let (bytes, stats) = progress.bytes_and_stats().await?;
tokio::io::stdout().write_all(&bytes).await?;
stats
};
tracing::info!("Stream done with stats: {stats:?}");
Ok(())
}