file_sharing_example/
main.rs1#![doc = include_str!("../README.md")]
22
23mod network;
24
25use std::{error::Error, io::Write, path::PathBuf};
26
27use clap::Parser;
28use futures::{prelude::*, StreamExt};
29use libp2p::{core::Multiaddr, multiaddr::Protocol};
30use tokio::task::spawn;
31use tracing_subscriber::EnvFilter;
32
33#[tokio::main]
34async fn main() -> Result<(), Box<dyn Error>> {
35 let _ = tracing_subscriber::fmt()
36 .with_env_filter(EnvFilter::from_default_env())
37 .try_init();
38
39 let opt = Opt::parse();
40
41 let (mut network_client, mut network_events, network_event_loop) =
42 network::new(opt.secret_key_seed).await?;
43
44 spawn(network_event_loop.run());
46
47 match opt.listen_address {
50 Some(addr) => network_client
51 .start_listening(addr)
52 .await
53 .expect("Listening not to fail."),
54 None => network_client
55 .start_listening("/ip4/0.0.0.0/tcp/0".parse()?)
56 .await
57 .expect("Listening not to fail."),
58 };
59
60 if let Some(addr) = opt.peer {
62 let Some(Protocol::P2p(peer_id)) = addr.iter().last() else {
63 return Err("Expect peer multiaddr to contain peer ID.".into());
64 };
65 network_client
66 .dial(peer_id, addr)
67 .await
68 .expect("Dial to succeed");
69 }
70
71 match opt.argument {
72 CliArgument::Provide { path, name } => {
74 network_client.start_providing(name.clone()).await;
76
77 loop {
78 match network_events.next().await {
79 Some(network::Event::InboundRequest { request, channel }) => {
81 if request == name {
82 network_client
83 .respond_file(std::fs::read(&path)?, channel)
84 .await;
85 }
86 }
87 e => todo!("{:?}", e),
88 }
89 }
90 }
91 CliArgument::Get { name } => {
93 let providers = network_client.get_providers(name.clone()).await;
95 if providers.is_empty() {
96 return Err(format!("Could not find provider for file {name}.").into());
97 }
98
99 let requests = providers.into_iter().map(|p| {
101 let mut network_client = network_client.clone();
102 let name = name.clone();
103 async move { network_client.request_file(p, name).await }.boxed()
104 });
105
106 let file_content = futures::future::select_ok(requests)
108 .await
109 .map_err(|_| "None of the providers returned file.")?
110 .0;
111
112 std::io::stdout().write_all(&file_content)?;
113 }
114 }
115
116 Ok(())
117}
118
119#[derive(Parser, Debug)]
120#[command(name = "libp2p file sharing example")]
121struct Opt {
122 #[arg(long)]
124 secret_key_seed: Option<u8>,
125
126 #[arg(long)]
127 peer: Option<Multiaddr>,
128
129 #[arg(long)]
130 listen_address: Option<Multiaddr>,
131
132 #[command(subcommand)]
133 argument: CliArgument,
134}
135
136#[derive(Debug, Parser)]
137enum CliArgument {
138 Provide {
139 #[arg(long)]
140 path: PathBuf,
141 #[arg(long)]
142 name: String,
143 },
144 Get {
145 #[arg(long)]
146 name: String,
147 },
148}