Strand Blobs
Blob Store
Put async-readable bytes, retrieve a reader and inspect offset-based listing.
BlobStore::new(metadata_strand, data_strand, config).await builds the index from persisted records. put takes an AsyncRead + Unpin source and optional JSON metadata. ListOptions uses offset/limit and optional metadata filtering; ListResult contains blobs, total and has_more, not a continuation cursor. A missing/deleted/unusable blob can produce None; other storage/integrity failures are errors.
This example is checked against source signatures; it was not compiled or run during this documentation pass.
use strand::{Strand, StrandConfig};
use strand_blobs::{BlobConfig, BlobStore, ListOptions};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// BlobStore is built from two strands: one for metadata, one for chunked data.
// It deduplicates by BLAKE3 chunk hash and tracks usage in dedup_stats().
let dir = tempfile::tempdir()?;
let meta = Strand::new(StrandConfig::new().with_storage(dir.path().join("meta"))).await?;
let data = Strand::new(StrandConfig::new().with_storage(dir.path().join("data"))).await?;
let store = BlobStore::new(meta, data, BlobConfig::default()).await?;
// put() ingests bytes and returns a BLAKE3-derived BlobId.
let id = store.put(b"a binary blob, content-addressed by BLAKE3".as_ref(), None).await?;
let _reader = store.get(&id).await?.expect("blob present");
// list() uses offset/limit and reports whether more records exist.
let listing = store.list(ListOptions::default()).await?;
let dedup = store.dedup_stats().await?;
println!(
"blobs_listed={} dedup_unique_chunks={} dedup_logical={}",
listing.blobs.len(),
dedup.unique_chunks,
dedup.total_chunks,
);
Ok(())
}