ipfrs_cli/commands/
block.rs

1//! Block operation commands
2//!
3//! This module provides raw block operations:
4//! - `block_get` - Get raw block data
5//! - `block_stat` - Show block statistics
6//! - `block_put` - Store raw block
7//! - `block_rm` - Remove block
8//! - `list_blocks` - List all stored blocks
9
10use anyhow::Result;
11
12use crate::output::{self, error, format_bytes, print_cid, print_kv, success};
13use crate::progress;
14
15/// Get raw block data
16pub async fn block_get(cid_str: String) -> Result<()> {
17    use ipfrs_core::Cid;
18    use ipfrs_storage::{BlockStoreConfig, BlockStoreTrait, SledBlockStore};
19
20    let cid = cid_str
21        .parse::<Cid>()
22        .map_err(|e| anyhow::anyhow!("Invalid CID: {}", e))?;
23
24    let config = BlockStoreConfig::default();
25    let store = SledBlockStore::new(config)?;
26
27    match store.get(&cid).await? {
28        Some(block) => {
29            use std::io::Write;
30            std::io::stdout().write_all(block.data())?;
31            Ok(())
32        }
33        None => {
34            eprintln!("Block not found: {}", cid);
35            std::process::exit(1);
36        }
37    }
38}
39
40/// Show block statistics
41pub async fn block_stat(cid_str: String, format: &str) -> Result<()> {
42    use ipfrs_core::Cid;
43    use ipfrs_storage::{BlockStoreConfig, BlockStoreTrait, SledBlockStore};
44
45    let cid = cid_str
46        .parse::<Cid>()
47        .map_err(|e| anyhow::anyhow!("Invalid CID: {}", e))?;
48
49    let config = BlockStoreConfig::default();
50    let store = SledBlockStore::new(config)?;
51
52    match store.get(&cid).await? {
53        Some(block) => {
54            match format {
55                "json" => {
56                    println!("{{");
57                    println!("  \"cid\": \"{}\",", cid);
58                    println!("  \"size\": {}", block.size());
59                    println!("}}");
60                }
61                _ => {
62                    println!("CID: {}", cid);
63                    println!("Size: {} bytes", block.size());
64                }
65            }
66            Ok(())
67        }
68        None => {
69            eprintln!("Block not found: {}", cid);
70            std::process::exit(1);
71        }
72    }
73}
74
75/// Store raw block
76pub async fn block_put(path: String, format: &str) -> Result<()> {
77    use bytes::Bytes;
78    use ipfrs_core::Block;
79    use ipfrs_storage::{BlockStoreConfig, BlockStoreTrait, SledBlockStore};
80
81    let file_path = std::path::Path::new(&path);
82    let filename = file_path
83        .file_name()
84        .map(|s| s.to_string_lossy().to_string())
85        .unwrap_or_else(|| path.clone());
86
87    // Read raw block data
88    let pb = progress::spinner(&format!("Reading {}", filename));
89    let data = tokio::fs::read(&path).await?;
90    let size = data.len() as u64;
91    let bytes_data = Bytes::from(data);
92    progress::finish_spinner_success(&pb, &format!("Read {} bytes", size));
93
94    // Create block from raw data
95    let pb = progress::spinner("Creating block");
96    let block = Block::new(bytes_data)?;
97    let cid = *block.cid();
98    progress::finish_spinner_success(&pb, "Block created");
99
100    // Initialize storage
101    let config = BlockStoreConfig::default();
102    let store = SledBlockStore::new(config)?;
103
104    // Store block
105    let pb = progress::spinner("Storing raw block");
106    store.put(&block).await?;
107    progress::finish_spinner_success(&pb, "Block stored");
108
109    match format {
110        "json" => {
111            println!("{{");
112            println!("  \"cid\": \"{}\",", cid);
113            println!("  \"size\": {}", block.size());
114            println!("}}");
115        }
116        _ => {
117            success("Raw block stored");
118            print_cid("CID", &cid.to_string());
119            print_kv("Size", &format_bytes(block.size()));
120        }
121    }
122
123    Ok(())
124}
125
126/// Remove block
127pub async fn block_rm(cid_str: String, force: bool) -> Result<()> {
128    use ipfrs_core::Cid;
129    use ipfrs_storage::{BlockStoreConfig, BlockStoreTrait, SledBlockStore};
130    use std::io::{self, Write};
131
132    let cid = cid_str
133        .parse::<Cid>()
134        .map_err(|e| anyhow::anyhow!("Invalid CID: {}", e))?;
135
136    let config = BlockStoreConfig::default();
137    let store = SledBlockStore::new(config)?;
138
139    if !store.has(&cid).await? {
140        error(&format!("Block not found: {}", cid));
141        std::process::exit(1);
142    }
143
144    // Confirm deletion unless --force is used
145    if !force {
146        print!("Remove block {}? [y/N] ", cid);
147        io::stdout().flush()?;
148
149        let mut input = String::new();
150        io::stdin().read_line(&mut input)?;
151
152        if !input.trim().eq_ignore_ascii_case("y") {
153            output::info("Aborted");
154            return Ok(());
155        }
156    }
157
158    store.delete(&cid).await?;
159    success(&format!("Removed block: {}", cid));
160
161    Ok(())
162}
163
164/// List all stored blocks
165pub async fn list_blocks(format: &str) -> Result<()> {
166    use ipfrs_storage::{BlockStoreConfig, BlockStoreTrait, SledBlockStore};
167
168    // Initialize storage
169    let config = BlockStoreConfig::default();
170    let store = SledBlockStore::new(config)?;
171
172    // List all CIDs
173    let cids = store.list_cids()?;
174
175    if cids.is_empty() {
176        match format {
177            "json" => println!("[]"),
178            _ => println!("No blocks stored"),
179        }
180    } else {
181        match format {
182            "json" => {
183                println!("[");
184                for (i, cid) in cids.iter().enumerate() {
185                    if let Some(block) = store.get(cid).await? {
186                        print!("  {{");
187                        print!("\"cid\": \"{}\", ", cid);
188                        print!("\"size\": {}", block.size());
189                        if i < cids.len() - 1 {
190                            println!("}},");
191                        } else {
192                            println!("}}");
193                        }
194                    }
195                }
196                println!("]");
197            }
198            _ => {
199                println!("Stored blocks ({} total):", cids.len());
200                for cid in cids {
201                    if let Some(block) = store.get(&cid).await? {
202                        println!("  {} ({} bytes)", cid, block.size());
203                    }
204                }
205            }
206        }
207    }
208
209    Ok(())
210}