ipfrs_cli/commands/
repo.rs

1//! Repository management commands
2//!
3//! This module provides repository operations:
4//! - `repo_gc` - Run garbage collection
5//! - `repo_stat` - Show repository statistics
6//! - `repo_fsck` - Verify repository integrity
7//! - `repo_version` - Show repository version
8
9use anyhow::Result;
10
11use crate::output::{self, error, format_bytes, print_header, print_kv, success};
12use crate::progress;
13
14use super::stats::stats_repo;
15
16/// Run garbage collection
17pub async fn repo_gc(dry_run: bool, format: &str) -> Result<()> {
18    use ipfrs::{Node, NodeConfig};
19
20    let action = if dry_run { "Analyzing" } else { "Running" };
21    let pb = progress::spinner(&format!("{} garbage collection", action));
22    let mut node = Node::new(NodeConfig::default())?;
23    node.start().await?;
24
25    let result = node.repo_gc(dry_run).await?;
26    progress::finish_spinner_success(&pb, "GC complete");
27
28    match format {
29        "json" => {
30            println!("{{");
31            println!("  \"blocks_collected\": {},", result.blocks_collected);
32            println!("  \"bytes_freed\": {},", result.bytes_freed);
33            println!("  \"blocks_marked\": {},", result.blocks_marked);
34            println!("  \"blocks_scanned\": {},", result.blocks_scanned);
35            println!("  \"duration_ms\": {}", result.duration.as_millis());
36            println!("}}");
37        }
38        _ => {
39            print_header("Garbage Collection Results");
40            print_kv("Blocks scanned", &result.blocks_scanned.to_string());
41            print_kv("Blocks marked", &result.blocks_marked.to_string());
42            print_kv("Blocks collected", &result.blocks_collected.to_string());
43            print_kv("Bytes freed", &format_bytes(result.bytes_freed));
44            print_kv(
45                "Duration",
46                &format!("{:.2}s", result.duration.as_secs_f64()),
47            );
48
49            if dry_run {
50                output::warning("Dry run - no blocks were actually deleted");
51            } else if result.blocks_collected > 0 {
52                success(&format!(
53                    "Freed {} from {} blocks",
54                    format_bytes(result.bytes_freed),
55                    result.blocks_collected
56                ));
57            } else {
58                output::info("No unreferenced blocks found");
59            }
60        }
61    }
62
63    node.stop().await?;
64    Ok(())
65}
66
67/// Show repository statistics
68pub async fn repo_stat(format: &str) -> Result<()> {
69    // Reuse the existing stats_repo function
70    stats_repo(format).await
71}
72
73/// Verify repository integrity
74pub async fn repo_fsck(format: &str) -> Result<()> {
75    use ipfrs::{Node, NodeConfig};
76
77    let pb = progress::spinner("Checking repository integrity");
78    let mut node = Node::new(NodeConfig::default())?;
79    node.start().await?;
80
81    let result = node.repo_fsck().await?;
82    progress::finish_spinner_success(&pb, "Integrity check complete");
83
84    match format {
85        "json" => {
86            println!("{{");
87            println!("  \"blocks_checked\": {},", result.blocks_checked);
88            println!("  \"blocks_valid\": {},", result.blocks_valid);
89            println!("  \"blocks_corrupt\": {},", result.blocks_corrupt.len());
90            println!("  \"blocks_missing\": {}", result.blocks_missing.len());
91            println!("}}");
92        }
93        _ => {
94            print_header("Repository Integrity Check");
95            print_kv("Blocks checked", &result.blocks_checked.to_string());
96            print_kv("Valid blocks", &result.blocks_valid.to_string());
97            print_kv("Corrupt blocks", &result.blocks_corrupt.len().to_string());
98            print_kv("Missing blocks", &result.blocks_missing.len().to_string());
99
100            if result.blocks_corrupt.is_empty() && result.blocks_missing.is_empty() {
101                success("Repository integrity verified - no issues found");
102            } else {
103                if !result.blocks_corrupt.is_empty() {
104                    println!("\nCorrupt blocks:");
105                    for cid in &result.blocks_corrupt {
106                        error(&format!("  {}", cid));
107                    }
108                }
109                if !result.blocks_missing.is_empty() {
110                    println!("\nMissing blocks:");
111                    for cid in &result.blocks_missing {
112                        error(&format!("  {}", cid));
113                    }
114                }
115            }
116        }
117    }
118
119    node.stop().await?;
120    Ok(())
121}
122
123/// Show repository version
124pub async fn repo_version(_format: &str) -> Result<()> {
125    print_header("Repository Version");
126    print_kv("Format version", "1");
127    print_kv("IPFRS version", env!("CARGO_PKG_VERSION"));
128    Ok(())
129}