11use std:: path:: PathBuf ;
22
3- use iroh:: { protocol:: Router , Endpoint } ;
3+ use clap:: Parser ;
4+ use iroh:: { discovery:: pkarr:: PkarrResolver , protocol:: Router , Endpoint } ;
45use iroh_blobs:: { store:: mem:: MemStore , ticket:: BlobTicket , BlobsProtocol } ;
6+ mod common;
7+ use common:: setup_logging;
8+ #[ derive( Debug , Parser ) ]
9+ #[ command( version, about) ]
10+ pub struct Cli {
11+ #[ clap( subcommand) ]
12+ command : Command ,
13+ }
514
6- #[ tokio:: main]
7- async fn main ( ) -> anyhow:: Result < ( ) > {
15+ #[ derive( Parser , Debug ) ]
16+ pub enum Command {
17+ /// Send a file to the network
18+ Send {
19+ /// Path to the file to send
20+ file : PathBuf ,
21+ } ,
22+ /// Receive a file from the network
23+ Receive {
24+ /// Ticket describing the content to fetch
25+ ticket : BlobTicket ,
26+ /// Path to save the received file
27+ filename : PathBuf ,
28+ } ,
29+ }
30+
31+ async fn send ( filename : PathBuf ) -> anyhow:: Result < ( ) > {
832 // Create an endpoint, it allows creating and accepting
933 // connections in the iroh p2p world
1034 let endpoint = Endpoint :: builder ( ) . discovery_n0 ( ) . bind ( ) . await ?;
@@ -14,80 +38,82 @@ async fn main() -> anyhow::Result<()> {
1438 // Then we initialize a struct that can accept blobs requests over iroh connections
1539 let blobs = BlobsProtocol :: new ( & store, endpoint. clone ( ) , None ) ;
1640
17- // Grab all passed in arguments, the first one is the binary itself, so we skip it.
18- let args: Vec < String > = std:: env:: args ( ) . skip ( 1 ) . collect ( ) ;
19- // Convert to &str, so we can pattern-match easily:
20- let arg_refs: Vec < & str > = args. iter ( ) . map ( String :: as_str) . collect ( ) ;
41+ let abs_path = std:: path:: absolute ( & filename) ?;
42+ let tag = store. blobs ( ) . add_path ( abs_path) . await ?;
43+
44+ let node_id = endpoint. node_id ( ) ;
45+ let ticket = BlobTicket :: new ( node_id. into ( ) , tag. hash , tag. format ) ;
2146
22- match arg_refs. as_slice ( ) {
23- [ "send" , filename] => {
24- let filename: PathBuf = filename. parse ( ) ?;
25- let abs_path = std:: path:: absolute ( & filename) ?;
47+ println ! ( "File hashed. Fetch this file by running:" ) ;
48+ println ! (
49+ "cargo run --example transfer -- receive {ticket} {}" ,
50+ filename. display( )
51+ ) ;
2652
27- println ! ( "Hashing file." ) ;
53+ // For sending files we build a router that accepts blobs connections & routes them
54+ // to the blobs protocol.
55+ let router = Router :: builder ( endpoint)
56+ . accept ( iroh_blobs:: ALPN , blobs)
57+ . spawn ( ) ;
2858
29- // When we import a blob, we get back a "tag" that refers to said blob in the store
30- // and allows us to control when/if it gets garbage-collected
31- let tag = store. blobs ( ) . add_path ( abs_path) . await ?;
59+ tokio:: signal:: ctrl_c ( ) . await ?;
3260
33- let node_id = endpoint. node_id ( ) ;
34- let ticket = BlobTicket :: new ( node_id. into ( ) , tag. hash , tag. format ) ;
61+ // Gracefully shut down the node
62+ println ! ( "Shutting down." ) ;
63+ router. shutdown ( ) . await ?;
64+ Ok ( ( ) )
65+ }
3566
36- println ! ( "File hashed. Fetch this file by running:" ) ;
37- println ! (
38- "cargo run --example transfer -- receive {ticket} {}" ,
39- filename. display( )
40- ) ;
67+ async fn receive ( ticket : BlobTicket , filename : PathBuf ) -> anyhow:: Result < ( ) > {
68+ // Create a store to download blobs into
69+ let store = MemStore :: new ( ) ;
4170
42- // For sending files we build a router that accepts blobs connections & routes them
43- // to the blobs protocol.
44- let router = Router :: builder ( endpoint)
45- . accept ( iroh_blobs:: ALPN , blobs)
46- . spawn ( ) ;
71+ // Create an endpoint, it allows creating and accepting
72+ // connections in the iroh p2p world.
73+ //
74+ // Since we just want to receive files, we don't need a stable node address
75+ // or to publish our discovery information.
76+ let endpoint = Endpoint :: builder ( )
77+ . discovery ( PkarrResolver :: n0_dns ( ) )
78+ . bind ( )
79+ . await ?;
4780
48- tokio:: signal:: ctrl_c ( ) . await ?;
81+ // For receiving files, we create a "downloader" that allows us to fetch files
82+ // from other nodes via iroh connections
83+ let downloader = store. downloader ( & endpoint) ;
4984
50- // Gracefully shut down the node
51- println ! ( "Shutting down." ) ;
52- router. shutdown ( ) . await ?;
53- }
54- [ "receive" , ticket, filename] => {
55- let filename: PathBuf = filename. parse ( ) ?;
56- let abs_path = std:: path:: absolute ( filename) ?;
57- let ticket: BlobTicket = ticket. parse ( ) ?;
85+ println ! ( "Starting download." ) ;
5886
59- // For receiving files, we create a " downloader" that allows us to fetch files
60- // from other nodes via iroh connections
61- let downloader = store . downloader ( & endpoint ) ;
87+ downloader
88+ . download ( ticket . hash ( ) , [ ticket . node_addr ( ) . node_id ] )
89+ . await ? ;
6290
63- println ! ( "Starting download." ) ;
91+ println ! ( "Finished download." ) ;
92+ println ! ( "Copying to destination." ) ;
6493
65- downloader
66- . download ( ticket. hash ( ) , Some ( ticket. node_addr ( ) . node_id ) )
67- . await ?;
94+ store. export ( ticket. hash ( ) , filename) . await ?;
6895
69- println ! ( "Finished download." ) ;
70- println ! ( "Copying to destination." ) ;
96+ println ! ( "Finished copying." ) ;
7197
72- store. blobs ( ) . export ( ticket. hash ( ) , abs_path) . await ?;
98+ // Gracefully shut down the endpoint and the store
99+ println ! ( "Shutting down." ) ;
100+ endpoint. close ( ) . await ;
101+ store. shutdown ( ) . await ?;
102+ Ok ( ( ) )
103+ }
73104
74- println ! ( "Finished copying." ) ;
105+ #[ tokio:: main]
106+ async fn main ( ) -> anyhow:: Result < ( ) > {
107+ setup_logging ( ) ;
108+ let cli = Cli :: parse ( ) ;
75109
76- // Gracefully shut down the node
77- println ! ( "Shutting down." ) ;
78- endpoint . close ( ) . await ;
110+ match cli . command {
111+ Command :: Send { file } => {
112+ send ( file ) . await ? ;
79113 }
80- _ => {
81- println ! ( "Couldn't parse command line arguments: {args:?}" ) ;
82- println ! ( "Usage:" ) ;
83- println ! ( " # to send:" ) ;
84- println ! ( " cargo run --example transfer -- send [FILE]" ) ;
85- println ! ( " # this will print a ticket." ) ;
86- println ! ( ) ;
87- println ! ( " # to receive:" ) ;
88- println ! ( " cargo run --example transfer -- receive [TICKET] [FILE]" ) ;
114+ Command :: Receive { ticket, filename } => {
115+ receive ( ticket, filename) . await ?;
89116 }
90117 }
91-
92118 Ok ( ( ) )
93119}
0 commit comments