Skip to content

Commit 8a00f47

Browse files
committed
docs(examples): add transfer-collection example
1 parent 4645452 commit 8a00f47

File tree

2 files changed

+127
-1
lines changed

2 files changed

+127
-1
lines changed

examples/transfer-collection.rs

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
//! Example that shows how to create a collection, and transfer it to another
2+
//! node. It also shows patterns for defining a "Node" struct in higher-level
3+
//! code that abstracts over these operations with an API that feels closer to
4+
//! what an application would use.
5+
//!
6+
//! Run the entire example in one command:
7+
//! $ cargo run --example transfer-collection
8+
use std::collections::HashMap;
9+
10+
use anyhow::{Context, Result};
11+
use iroh::{protocol::Router, Endpoint, NodeAddr, Watcher};
12+
use iroh_blobs::{
13+
api::{downloader::Shuffled, Store, TempTag},
14+
format::collection::Collection,
15+
store::mem::MemStore,
16+
BlobsProtocol, Hash, HashAndFormat,
17+
};
18+
19+
/// Node is something you'd define in your application. It can contain whatever
20+
/// shared state you'd want to couple with network operations.
21+
struct Node {
22+
store: Store,
23+
/// Router with the blobs protocol registered, to accept blobs requests.
24+
/// We can always get the endpoint with router.endpoint()
25+
router: Router,
26+
}
27+
28+
impl Node {
29+
async fn new() -> Result<Self> {
30+
let endpoint = Endpoint::builder().bind().await?;
31+
32+
let store = MemStore::new();
33+
34+
// this BlobsProtocol accepts connections from other nodes and serves blobs from the store
35+
// we pass None to skip subscribing to request events
36+
let blobs = BlobsProtocol::new(&store, endpoint.clone(), None);
37+
// Routers group one or more protocols together to accept connections from other nodes,
38+
// here we're only using one, but could add more in a real world use case as needed
39+
let router = Router::builder(endpoint)
40+
.accept(iroh_blobs::ALPN, blobs)
41+
.spawn();
42+
43+
Ok(Self {
44+
store: store.into(),
45+
router,
46+
})
47+
}
48+
49+
// get address of this node. Has the side effect of waiting for the node
50+
// to be online & ready to accept connections
51+
async fn node_addr(&self) -> Result<NodeAddr> {
52+
let addr = self.router.endpoint().node_addr().initialized().await;
53+
Ok(addr)
54+
}
55+
56+
async fn list_hashes(&self) -> Result<Vec<Hash>> {
57+
self.store
58+
.blobs()
59+
.list()
60+
.hashes()
61+
.await
62+
.context("Failed to list hashes")
63+
}
64+
65+
/// creates a collection from a given set of named blobs, adds it to the local store
66+
/// and returns the hash of the collection.
67+
async fn create_collection(&self, named_blobs: Vec<(&str, Vec<u8>)>) -> Result<Hash> {
68+
let mut collection_items: HashMap<&str, TempTag> = HashMap::new();
69+
70+
let tx = self.store.batch().await?;
71+
for (name, data) in named_blobs {
72+
let tmp_tag = tx.add_slice(data).await?;
73+
collection_items.insert(name, tmp_tag);
74+
}
75+
76+
let collection_items = collection_items
77+
.iter()
78+
.map(|(name, tag)| {
79+
let hash = tag.hash().clone();
80+
(name.to_string(), hash)
81+
})
82+
.collect::<Vec<_>>();
83+
84+
let collection = Collection::from_iter(collection_items);
85+
86+
let collection = collection.store(&self.store).await?;
87+
let hash = collection.hash().clone();
88+
self.store.tags().create(collection).await?;
89+
Ok(hash)
90+
}
91+
92+
/// retrive an entire collection from a given hash and provider
93+
async fn get_collection(&self, hash: Hash, provider: NodeAddr) -> Result<()> {
94+
self.router.endpoint().add_node_addr(provider.clone())?;
95+
let req = HashAndFormat::hash_seq(hash);
96+
let addrs = Shuffled::new(vec![provider.node_id]);
97+
self.store
98+
.downloader(self.router.endpoint())
99+
.download(req, addrs)
100+
.await?;
101+
Ok(())
102+
}
103+
}
104+
105+
#[tokio::main]
106+
async fn main() -> anyhow::Result<()> {
107+
let send_node = Node::new().await?;
108+
let send_node_addr = send_node.node_addr().await?;
109+
let hash = send_node
110+
.create_collection(vec![
111+
("a.txt", b"this is file a".into()),
112+
("b.txt", b"this is file b".into()),
113+
("c.txt", b"this is file c".into()),
114+
])
115+
.await?;
116+
117+
let recv_node = Node::new().await?;
118+
recv_node.get_collection(hash, send_node_addr).await?;
119+
120+
let send_hashes = send_node.list_hashes().await?;
121+
let recv_hashes = recv_node.list_hashes().await?;
122+
assert_eq!(send_hashes.len(), recv_hashes.len());
123+
124+
println!("Transfer complete!");
125+
Ok(())
126+
}

src/api/blobs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -620,7 +620,7 @@ pub struct AddPathOptions {
620620
/// stream directly can be inconvenient, so this struct provides some convenience
621621
/// methods to work with the result.
622622
///
623-
/// It also implements [`IntoFuture`], so you can await it to get the [`TempTag`] that
623+
/// It also implements [`IntoFuture`], so you can await it to get the [`TagInfo`] that
624624
/// contains the hash of the added content and also protects the content.
625625
///
626626
/// If you want access to the stream, you can use the [`AddProgress::stream`] method.

0 commit comments

Comments
 (0)