ipfrs_cli/commands/
network.rs

1//! Network commands (swarm, dht, bootstrap)
2//!
3//! This module provides network-related operations:
4//! - Swarm: peers, connect, disconnect, addrs
5//! - DHT: findprovs, findpeer, provide
6//! - Bootstrap: list, add, rm
7
8use anyhow::Result;
9
10use crate::output::{self, print_header, success};
11use crate::progress;
12
13// ============================================================================
14// Swarm Commands
15// ============================================================================
16
17/// Show connected peers
18pub async fn show_peers(format: &str) -> Result<()> {
19    use ipfrs::{Node, NodeConfig};
20
21    let mut node = Node::new(NodeConfig::default())?;
22    node.start().await?;
23
24    let peers = node.peers().await?;
25
26    match format {
27        "json" => {
28            println!("[");
29            for (i, peer) in peers.iter().enumerate() {
30                if i > 0 {
31                    println!(",");
32                }
33                print!("  \"{}\"", peer);
34            }
35            println!();
36            println!("]");
37        }
38        _ => {
39            if peers.is_empty() {
40                println!("No connected peers");
41            } else {
42                println!("Connected peers ({}):", peers.len());
43                for peer in &peers {
44                    println!("  {}", peer);
45                }
46            }
47        }
48    }
49
50    node.stop().await?;
51    Ok(())
52}
53
54/// Connect to a peer
55pub async fn swarm_connect(addr: &str) -> Result<()> {
56    use ipfrs::{Node, NodeConfig};
57
58    let mut node = Node::new(NodeConfig::default())?;
59    node.start().await?;
60
61    println!("Connecting to {}...", addr);
62    node.connect(addr).await?;
63    println!("Connection initiated");
64
65    node.stop().await?;
66    Ok(())
67}
68
69/// Disconnect from a peer
70pub async fn swarm_disconnect(peer_id: &str) -> Result<()> {
71    use ipfrs::{Node, NodeConfig};
72
73    let mut node = Node::new(NodeConfig::default())?;
74    node.start().await?;
75
76    println!("Disconnecting from {}...", peer_id);
77    node.disconnect(peer_id).await?;
78    println!("Disconnected");
79
80    node.stop().await?;
81    Ok(())
82}
83
84/// List listening addresses
85pub async fn swarm_addrs(format: &str) -> Result<()> {
86    use ipfrs::{Node, NodeConfig};
87
88    let mut node = Node::new(NodeConfig::default())?;
89    node.start().await?;
90
91    let stats = node.network_stats()?;
92
93    match format {
94        "json" => {
95            println!("[");
96            for (i, addr) in stats.listen_addrs.iter().enumerate() {
97                if i > 0 {
98                    println!(",");
99                }
100                print!("  \"{}\"", addr);
101            }
102            println!();
103            println!("]");
104        }
105        _ => {
106            if stats.listen_addrs.is_empty() {
107                println!("No listening addresses");
108            } else {
109                println!("Listening addresses:");
110                for addr in &stats.listen_addrs {
111                    println!("  {}", addr);
112                }
113            }
114        }
115    }
116
117    node.stop().await?;
118    Ok(())
119}
120
121// ============================================================================
122// DHT Commands
123// ============================================================================
124
125/// Find providers for a CID
126pub async fn dht_findprovs(cid: &str, _format: &str) -> Result<()> {
127    use ipfrs::{Node, NodeConfig};
128
129    let cid_parsed: ipfrs_core::Cid = cid
130        .parse()
131        .map_err(|e| anyhow::anyhow!("Invalid CID: {}", e))?;
132
133    let mut node = Node::new(NodeConfig::default())?;
134    node.start().await?;
135
136    println!("Searching for providers of {}...", cid);
137    node.find_providers(&cid_parsed).await?;
138
139    // In a real implementation, we'd wait for provider events
140    // For now, just indicate the query was sent
141    println!("Provider query sent to DHT");
142    println!("(Provider discovery events would be received asynchronously)");
143
144    node.stop().await?;
145    Ok(())
146}
147
148/// Announce content to DHT
149pub async fn dht_provide(cid: &str) -> Result<()> {
150    use ipfrs::{Node, NodeConfig};
151
152    let cid_parsed: ipfrs_core::Cid = cid
153        .parse()
154        .map_err(|e| anyhow::anyhow!("Invalid CID: {}", e))?;
155
156    let mut node = Node::new(NodeConfig::default())?;
157    node.start().await?;
158
159    println!("Announcing {} to DHT...", cid);
160    node.provide(&cid_parsed).await?;
161    println!("Content announced to DHT");
162
163    node.stop().await?;
164    Ok(())
165}
166
167/// Find peer addresses in DHT
168pub async fn dht_findpeer(peer_id: &str, format: &str) -> Result<()> {
169    use ipfrs::{Node, NodeConfig};
170
171    let pb = progress::spinner(&format!("Looking up peer {}", peer_id));
172    let mut node = Node::new(NodeConfig::default())?;
173    node.start().await?;
174
175    // In a full implementation, this would query the DHT
176    // For now, we'll show a placeholder response
177    progress::finish_spinner_success(&pb, "Lookup complete");
178
179    match format {
180        "json" => {
181            println!("{{");
182            println!("  \"peer_id\": \"{}\",", peer_id);
183            println!("  \"addresses\": [],");
184            println!("  \"note\": \"DHT lookup placeholder - actual implementation pending\"");
185            println!("}}");
186        }
187        _ => {
188            print_header(&format!("Peer: {}", peer_id));
189            output::info("DHT peer lookup is a placeholder");
190            output::info("Full implementation requires peer routing protocol");
191        }
192    }
193
194    node.stop().await?;
195    Ok(())
196}
197
198// ============================================================================
199// Bootstrap Commands
200// ============================================================================
201
202/// List bootstrap peers
203pub async fn bootstrap_list(format: &str) -> Result<()> {
204    use ipfrs::{Node, NodeConfig};
205
206    let mut node = Node::new(NodeConfig::default())?;
207    node.start().await?;
208
209    let bootstrap_peers = node.bootstrap_peers()?;
210
211    match format {
212        "json" => {
213            println!("[");
214            for (i, addr) in bootstrap_peers.iter().enumerate() {
215                if i > 0 {
216                    println!(",");
217                }
218                print!("  \"{}\"", addr);
219            }
220            println!();
221            println!("]");
222        }
223        _ => {
224            if bootstrap_peers.is_empty() {
225                println!("No bootstrap peers configured");
226            } else {
227                println!("Bootstrap peers ({}):", bootstrap_peers.len());
228                for addr in &bootstrap_peers {
229                    println!("  {}", addr);
230                }
231            }
232        }
233    }
234
235    node.stop().await?;
236    Ok(())
237}
238
239/// Add a bootstrap peer
240pub async fn bootstrap_add(addr: &str) -> Result<()> {
241    use ipfrs::{Node, NodeConfig};
242
243    let pb = progress::spinner(&format!("Adding bootstrap peer {}", addr));
244    let mut node = Node::new(NodeConfig::default())?;
245    node.start().await?;
246
247    node.add_bootstrap_peer(addr).await?;
248    progress::finish_spinner_success(&pb, "Peer added");
249
250    success(&format!("Added bootstrap peer: {}", addr));
251
252    node.stop().await?;
253    Ok(())
254}
255
256/// Remove a bootstrap peer
257pub async fn bootstrap_rm(addr: &str) -> Result<()> {
258    use ipfrs::{Node, NodeConfig};
259
260    let pb = progress::spinner(&format!("Removing bootstrap peer {}", addr));
261    let mut node = Node::new(NodeConfig::default())?;
262    node.start().await?;
263
264    node.remove_bootstrap_peer(addr).await?;
265    progress::finish_spinner_success(&pb, "Peer removed");
266
267    success(&format!("Removed bootstrap peer: {}", addr));
268
269    node.stop().await?;
270    Ok(())
271}