ipfrs_cli/commands/
stats.rs

1//! Statistics commands
2//!
3//! This module provides statistics-related commands:
4//! - `stats_repo` - Repository statistics
5//! - `stats_bw` - Bandwidth statistics
6//! - `stats_bitswap` - Bitswap statistics
7//! - `print_info` - Show node info
8//! - `print_version` - Show version
9//! - `show_id` - Show peer identity
10//! - `ping_peer` - Ping a peer
11
12use anyhow::Result;
13
14use crate::output::{self, format_bytes, print_header, print_kv};
15use crate::progress;
16
17/// Show repository statistics
18pub async fn stats_repo(format: &str) -> Result<()> {
19    use ipfrs_storage::{BlockStoreConfig, BlockStoreTrait, SledBlockStore};
20
21    // Initialize storage
22    let config = BlockStoreConfig::default();
23    let storage_path = config.path.clone();
24    let store = SledBlockStore::new(config)?;
25
26    // Gather statistics
27    let cids = store.list_cids()?;
28    let num_blocks = cids.len();
29
30    // Calculate total size
31    let mut total_size: u64 = 0;
32    for cid in &cids {
33        if let Some(block) = store.get(cid).await? {
34            total_size += block.size();
35        }
36    }
37
38    match format {
39        "json" => {
40            println!("{{");
41            println!("  \"num_blocks\": {},", num_blocks);
42            println!("  \"total_size\": {},", total_size);
43            println!("  \"storage_path\": \"{}\"", storage_path.display());
44            println!("}}");
45        }
46        _ => {
47            print_header("IPFRS Repository Statistics");
48            print_kv("Number of blocks", &num_blocks.to_string());
49            print_kv("Total size", &format_bytes(total_size));
50            print_kv("Storage path", &storage_path.display().to_string());
51
52            if num_blocks > 0 {
53                let avg_size = total_size / num_blocks as u64;
54                print_kv("Average block size", &format_bytes(avg_size));
55            }
56        }
57    }
58
59    Ok(())
60}
61
62/// Show bandwidth statistics
63pub async fn stats_bw(format: &str) -> Result<()> {
64    use ipfrs::{Node, NodeConfig};
65
66    let pb = progress::spinner("Collecting bandwidth statistics");
67    let mut node = Node::new(NodeConfig::default())?;
68    node.start().await?;
69
70    let stats = node.network_stats()?;
71    progress::finish_spinner_success(&pb, "Statistics collected");
72
73    match format {
74        "json" => {
75            println!("{{");
76            println!("  \"total_in\": {},", stats.bytes_received);
77            println!("  \"total_out\": {},", stats.bytes_sent);
78            println!("  \"rate_in\": 0,");
79            println!("  \"rate_out\": 0");
80            println!("}}");
81        }
82        _ => {
83            print_header("Bandwidth Statistics");
84            print_kv("Total received", &format_bytes(stats.bytes_received));
85            print_kv("Total sent", &format_bytes(stats.bytes_sent));
86            print_kv("Connected peers", &stats.connected_peers.to_string());
87        }
88    }
89
90    node.stop().await?;
91    Ok(())
92}
93
94/// Show bitswap statistics
95pub async fn stats_bitswap(format: &str) -> Result<()> {
96    use ipfrs::{Node, NodeConfig};
97
98    let pb = progress::spinner("Collecting bitswap statistics");
99    let mut node = Node::new(NodeConfig::default())?;
100    node.start().await?;
101
102    let stats = node.bitswap_stats()?;
103    progress::finish_spinner_success(&pb, "Statistics collected");
104
105    match format {
106        "json" => {
107            println!("{{");
108            println!("  \"want_list_size\": {},", stats.want_list_size);
109            println!("  \"have_list_size\": {},", stats.have_list_size);
110            println!("  \"pending_requests\": {},", stats.pending_requests);
111            println!(
112                "  \"peers_with_pending_requests\": {}",
113                stats.peers_with_pending_requests
114            );
115            println!("}}");
116        }
117        _ => {
118            print_header("Bitswap Statistics");
119            print_kv("Want list size", &stats.want_list_size.to_string());
120            print_kv("Have list size", &stats.have_list_size.to_string());
121            print_kv("Pending requests", &stats.pending_requests.to_string());
122            print_kv(
123                "Peers with requests",
124                &stats.peers_with_pending_requests.to_string(),
125            );
126        }
127    }
128
129    node.stop().await?;
130    Ok(())
131}
132
133/// Print IPFRS info
134pub fn print_info() {
135    println!("IPFRS - Inter-Planet File RUST System");
136    println!("Version: 0.3.0 (The Fast & The Wise)");
137    println!();
138    println!("Architecture:");
139    println!("  Logical Layer:");
140    println!("    - Semantic Router (Vector Search / Logic Solver)");
141    println!("    - Differentiable Storage (Gradient Tracking)");
142    println!();
143    println!("  Physical Layer:");
144    println!("    - TensorSwap (Optimized Tensor Streaming)");
145    println!("    - Rust Native Store (Sled / ParityDB)");
146    println!("    - Network Stack (libp2p / QUIC)");
147    println!();
148    println!("Technology Stack:");
149    println!("  - Runtime: Tokio async");
150    println!("  - Network: libp2p, QUIC");
151    println!("  - Storage: Sled");
152    println!("  - Zero-Copy: Apache Arrow, Safetensors");
153    println!("  - Vector Search: HNSW");
154}
155
156/// Print version
157pub fn print_version() {
158    println!("ipfrs {}", env!("CARGO_PKG_VERSION"));
159}
160
161/// Show peer identity
162pub async fn show_id(format: &str) -> Result<()> {
163    use ipfrs::{Node, NodeConfig};
164
165    let mut node = Node::new(NodeConfig::default())?;
166    node.start().await?;
167
168    let peer_id = node.peer_id()?;
169    let stats = node.network_stats()?;
170
171    match format {
172        "json" => {
173            println!("{{");
174            println!("  \"peer_id\": \"{}\",", peer_id);
175            println!("  \"addresses\": [");
176            for (i, addr) in stats.listen_addrs.iter().enumerate() {
177                if i > 0 {
178                    println!(",");
179                }
180                print!("    \"{}\"", addr);
181            }
182            println!();
183            println!("  ]");
184            println!("}}");
185        }
186        _ => {
187            println!("Peer ID: {}", peer_id);
188            println!("Addresses:");
189            for addr in &stats.listen_addrs {
190                println!("  {}", addr);
191            }
192        }
193    }
194
195    node.stop().await?;
196    Ok(())
197}
198
199/// Ping a peer
200pub async fn ping_peer(peer_id: &str, count: u32) -> Result<()> {
201    use ipfrs::{Node, NodeConfig};
202    use std::time::Instant;
203
204    let mut node = Node::new(NodeConfig::default())?;
205    node.start().await?;
206
207    output::info(&format!("PING {} ({} pings)", peer_id, count));
208
209    let mut successful = 0u32;
210    let mut total_time = std::time::Duration::ZERO;
211
212    for i in 0..count {
213        let start = Instant::now();
214        match node.ping(peer_id).await {
215            Ok(_) => {
216                let elapsed = start.elapsed();
217                total_time += elapsed;
218                successful += 1;
219                println!("seq={} time={:.2}ms", i + 1, elapsed.as_secs_f64() * 1000.0);
220            }
221            Err(e) => {
222                println!("seq={} error: {}", i + 1, e);
223            }
224        }
225
226        if i < count - 1 {
227            tokio::time::sleep(std::time::Duration::from_secs(1)).await;
228        }
229    }
230
231    println!();
232    println!("--- {} ping statistics ---", peer_id);
233    println!(
234        "{} packets transmitted, {} received, {:.1}% packet loss",
235        count,
236        successful,
237        ((count - successful) as f64 / count as f64) * 100.0
238    );
239
240    if successful > 0 {
241        let avg_time = total_time.as_secs_f64() * 1000.0 / successful as f64;
242        println!("rtt avg = {:.2}ms", avg_time);
243    }
244
245    node.stop().await?;
246    Ok(())
247}