ipfrs_cli/commands/
dag.rs

1//! DAG (Directed Acyclic Graph) commands
2//!
3//! This module provides DAG operations:
4//! - `dag_get` - Get DAG node
5//! - `dag_put` - Store DAG node
6//! - `dag_resolve` - Resolve IPLD path
7//! - `dag_export` - Export DAG to CAR file
8//! - `dag_import` - Import DAG from CAR file
9
10use anyhow::Result;
11
12use crate::output::{error, format_bytes, print_cid, print_header, print_kv, success};
13use crate::progress;
14
15/// Get DAG node
16pub async fn dag_get(cid_str: &str, format: &str) -> Result<()> {
17    use ipfrs::{Node, NodeConfig};
18    use ipfrs_core::Cid;
19
20    let cid = cid_str
21        .parse::<Cid>()
22        .map_err(|e| anyhow::anyhow!("Invalid CID: {}", e))?;
23
24    let pb = progress::spinner(&format!("Retrieving DAG node {}", cid));
25    let mut node = Node::new(NodeConfig::default())?;
26    node.start().await?;
27
28    match node.dag_get(&cid).await? {
29        Some(ipld) => {
30            progress::finish_spinner_success(&pb, "DAG node retrieved");
31
32            match format {
33                "json" => {
34                    // Serialize IPLD to JSON
35                    let json = serde_json::to_string_pretty(&ipld)?;
36                    println!("{}", json);
37                }
38                _ => {
39                    // Text format - pretty print IPLD
40                    print_header(&format!("DAG Node: {}", cid));
41                    let json = serde_json::to_string_pretty(&ipld)?;
42                    println!("{}", json);
43                }
44            }
45        }
46        None => {
47            progress::finish_spinner_error(&pb, "DAG node not found");
48            error(&format!("DAG node not found: {}", cid));
49            std::process::exit(1);
50        }
51    }
52
53    node.stop().await?;
54    Ok(())
55}
56
57/// Store DAG node
58pub async fn dag_put(data: &str, format: &str) -> Result<()> {
59    use ipfrs::{Node, NodeConfig};
60
61    let pb = progress::spinner("Parsing JSON data");
62
63    // Parse JSON to IPLD
64    let ipld: ipfrs_core::Ipld =
65        serde_json::from_str(data).map_err(|e| anyhow::anyhow!("Invalid JSON: {}", e))?;
66
67    progress::finish_spinner_success(&pb, "JSON parsed");
68
69    let pb = progress::spinner("Storing DAG node");
70    let mut node = Node::new(NodeConfig::default())?;
71    node.start().await?;
72
73    let cid = node.dag_put(ipld).await?;
74    progress::finish_spinner_success(&pb, "DAG node stored");
75
76    match format {
77        "json" => {
78            println!("{{");
79            println!("  \"cid\": \"{}\"", cid);
80            println!("}}");
81        }
82        _ => {
83            success("DAG node stored");
84            print_cid("CID", &cid.to_string());
85        }
86    }
87
88    node.stop().await?;
89    Ok(())
90}
91
92/// Resolve IPLD path
93pub async fn dag_resolve(path_str: &str, format: &str) -> Result<()> {
94    use ipfrs::{Node, NodeConfig};
95    use ipfrs_core::Cid;
96
97    // Parse the path - format: /ipfs/CID/path/to/data or just CID/path
98    let parts: Vec<&str> = path_str.trim_start_matches("/ipfs/").split('/').collect();
99
100    if parts.is_empty() {
101        return Err(anyhow::anyhow!("Invalid path: {}", path_str));
102    }
103
104    let root_cid_str = parts[0];
105    let sub_path = if parts.len() > 1 {
106        parts[1..].join("/")
107    } else {
108        String::new()
109    };
110
111    let root_cid = root_cid_str
112        .parse::<Cid>()
113        .map_err(|e| anyhow::anyhow!("Invalid CID: {}", e))?;
114
115    let pb = progress::spinner(&format!("Resolving path: {}", path_str));
116    let mut node = Node::new(NodeConfig::default())?;
117    node.start().await?;
118
119    match node.dag_resolve(&root_cid, &sub_path).await? {
120        Some(resolved_cid) => {
121            progress::finish_spinner_success(&pb, "Path resolved");
122
123            match format {
124                "json" => {
125                    println!("{{");
126                    println!("  \"cid\": \"{}\"", resolved_cid);
127                    println!("}}");
128                }
129                _ => {
130                    success(&format!("Resolved: {}", path_str));
131                    print_cid("CID", &resolved_cid.to_string());
132                }
133            }
134        }
135        None => {
136            progress::finish_spinner_error(&pb, "Path not found");
137            error(&format!("Could not resolve path: {}", path_str));
138            std::process::exit(1);
139        }
140    }
141
142    node.stop().await?;
143    Ok(())
144}
145
146/// Export DAG to CAR file
147pub async fn dag_export(cid_str: &str, output_path: &str, format: &str) -> Result<()> {
148    use ipfrs::{Node, NodeConfig};
149    use ipfrs_core::Cid;
150
151    let cid = cid_str
152        .parse::<Cid>()
153        .map_err(|e| anyhow::anyhow!("Invalid CID: {}", e))?;
154
155    let pb = progress::spinner(&format!("Exporting DAG {} to {}", cid, output_path));
156    let mut node = Node::new(NodeConfig::default())?;
157    node.start().await?;
158
159    let stats = node.dag_export(&cid, output_path).await?;
160    progress::finish_spinner_success(&pb, "DAG exported successfully");
161
162    match format {
163        "json" => {
164            println!("{{");
165            println!("  \"blocks_exported\": {},", stats.blocks_exported);
166            println!("  \"bytes_exported\": {}", stats.bytes_exported);
167            println!("}}");
168        }
169        _ => {
170            success(&format!("Exported DAG to {}", output_path));
171            print_header("Export Statistics");
172            print_kv("Blocks exported", &stats.blocks_exported.to_string());
173            print_kv("Bytes exported", &format_bytes(stats.bytes_exported));
174            print_kv("File", output_path);
175        }
176    }
177
178    node.stop().await?;
179    Ok(())
180}
181
182/// Import DAG from CAR file
183pub async fn dag_import(car_path: &str, format: &str) -> Result<()> {
184    use ipfrs::{Node, NodeConfig};
185
186    let pb = progress::spinner(&format!("Importing DAG from {}", car_path));
187    let mut node = Node::new(NodeConfig::default())?;
188    node.start().await?;
189
190    let stats = node.dag_import(car_path).await?;
191    progress::finish_spinner_success(&pb, "DAG imported successfully");
192
193    match format {
194        "json" => {
195            println!("{{");
196            println!("  \"blocks_imported\": {},", stats.blocks_imported);
197            println!("  \"bytes_imported\": {}", stats.bytes_imported);
198            println!("}}");
199        }
200        _ => {
201            success(&format!("Imported DAG from {}", car_path));
202            print_header("Import Statistics");
203            print_kv("Blocks imported", &stats.blocks_imported.to_string());
204            print_kv("Bytes imported", &format_bytes(stats.bytes_imported));
205        }
206    }
207
208    node.stop().await?;
209    Ok(())
210}