ipfrs_cli/commands/
config.rs1use crate::config::Config;
10use crate::output::{self, error, print_kv, success};
11use anyhow::{Context, Result};
12
13pub async fn config_show(format: String) -> Result<()> {
15 let config = Config::load()?;
16
17 if format == "json" {
18 let json = serde_json::to_string_pretty(&config)?;
19 println!("{}", json);
20 } else {
21 println!("IPFRS Configuration");
22 println!("===================");
23 println!();
24
25 println!("General:");
26 println!("--------");
27 print_kv(
28 " Data directory",
29 &config.general.data_dir.display().to_string(),
30 );
31 print_kv(" Log level", &config.general.log_level);
32 print_kv(" Color output", &config.general.color.to_string());
33 print_kv(" Default format", &config.general.format);
34 println!();
35
36 println!("Storage:");
37 println!("--------");
38 print_kv(" Blocks path", &config.storage.blocks_path);
39 print_kv(" Cache size", &format_bytes(config.storage.cache_size));
40 print_kv(" WAL enabled", &config.storage.wal_enabled.to_string());
41 print_kv(" GC interval", &format!("{}s", config.storage.gc_interval));
42 println!();
43
44 println!("Network:");
45 println!("--------");
46 print_kv(
47 " Max connections",
48 &config.network.max_connections.to_string(),
49 );
50 println!(" Listen addresses:");
51 for addr in &config.network.listen_addrs {
52 println!(" - {}", addr);
53 }
54 println!();
55
56 println!("Gateway:");
57 println!("--------");
58 print_kv(" Listen address", &config.gateway.listen_addr);
59 println!();
60
61 println!("API:");
62 println!("----");
63 print_kv(" Listen address", &config.api.listen_addr);
64 print_kv(" Auth enabled", &config.api.auth_enabled.to_string());
65 print_kv(" Timeout", &format!("{}s", config.api.timeout));
66 if let Some(ref remote_url) = config.api.remote_url {
67 print_kv(" Remote URL", remote_url);
68 }
69 println!();
70 }
71
72 Ok(())
73}
74
75pub async fn config_export(output: String, format: String) -> Result<()> {
77 let config = Config::load()?;
78
79 let content = if format == "json" {
81 serde_json::to_string_pretty(&config)?
82 } else if format == "toml" {
83 toml::to_string_pretty(&config)?
84 } else if format == "yaml" {
85 serde_yaml::to_string(&config)?
86 } else {
87 anyhow::bail!(
88 "Unsupported export format: {}. Use json, toml, or yaml.",
89 format
90 );
91 };
92
93 let content_len = content.len();
94
95 std::fs::write(&output, content)
96 .with_context(|| format!("Failed to write configuration to {}", output))?;
97
98 success(&format!("Configuration exported to {}", output));
99 print_kv("Format", &format);
100 print_kv("Size", &format!("{} bytes", content_len));
101
102 Ok(())
103}
104
105pub async fn config_import(input: String, dry_run: bool) -> Result<()> {
107 let content = std::fs::read_to_string(&input)
109 .with_context(|| format!("Failed to read configuration from {}", input))?;
110
111 let format = if input.ends_with(".json") {
113 "json"
114 } else if input.ends_with(".toml") {
115 "toml"
116 } else if input.ends_with(".yaml") || input.ends_with(".yml") {
117 "yaml"
118 } else {
119 if content.trim().starts_with('{') {
121 "json"
122 } else if content.contains('[') && content.contains(']') {
123 "toml"
124 } else {
125 "yaml"
126 }
127 };
128
129 let config: Config = match format {
131 "json" => serde_json::from_str(&content)?,
132 "toml" => toml::from_str(&content)?,
133 "yaml" => serde_yaml::from_str(&content)?,
134 _ => anyhow::bail!("Could not detect configuration format"),
135 };
136
137 if dry_run {
138 output::info("Dry run mode - configuration validation only");
139 success("✓ Configuration is valid");
140 print_kv("Format detected", format);
141 println!();
142 println!("Configuration summary:");
143 print_kv(
144 " Data directory",
145 &config.general.data_dir.display().to_string(),
146 );
147 print_kv(" Log level", &config.general.log_level);
148 print_kv(
149 " Max connections",
150 &config.network.max_connections.to_string(),
151 );
152 println!();
153 output::info("Use without --dry-run to apply the configuration");
154 } else {
155 let config_path = Config::default_path()?;
157
158 if config_path.exists() {
160 let backup_path = config_path.with_extension("toml.backup");
161 std::fs::copy(&config_path, &backup_path)?;
162 output::info(&format!("Backed up existing config to {:?}", backup_path));
163 }
164
165 let toml_content = toml::to_string_pretty(&config)?;
167 std::fs::write(&config_path, toml_content)?;
168
169 success("Configuration imported successfully");
170 print_kv("Source", &input);
171 print_kv("Destination", &config_path.display().to_string());
172 print_kv("Format", format);
173 println!();
174 output::info("Restart the daemon to apply changes: ipfrs daemon restart");
175 }
176
177 Ok(())
178}
179
180pub async fn config_edit() -> Result<()> {
182 let config_path = Config::default_path()?;
183
184 if !config_path.exists() {
185 error("Configuration file does not exist");
186 println!();
187 output::info("Initialize a new configuration:");
188 println!(" ipfrs init");
189 return Ok(());
190 }
191
192 let editor = std::env::var("EDITOR")
194 .or_else(|_| std::env::var("VISUAL"))
195 .unwrap_or_else(|_| "vi".to_string());
196
197 println!("Opening configuration in {}...", editor);
198 print_kv("Config file", &config_path.display().to_string());
199 println!();
200
201 let status = std::process::Command::new(&editor)
203 .arg(&config_path)
204 .status()
205 .with_context(|| format!("Failed to open editor: {}", editor))?;
206
207 if status.success() {
208 success("Configuration editor closed");
209 println!();
210 output::info("Restart the daemon to apply changes: ipfrs daemon restart");
211 } else {
212 error("Editor exited with error");
213 }
214
215 Ok(())
216}
217
218fn format_bytes(bytes: u64) -> String {
220 const KB: u64 = 1024;
221 const MB: u64 = KB * 1024;
222 const GB: u64 = MB * 1024;
223
224 if bytes >= GB {
225 format!("{:.2} GB", bytes as f64 / GB as f64)
226 } else if bytes >= MB {
227 format!("{:.2} MB", bytes as f64 / MB as f64)
228 } else if bytes >= KB {
229 format!("{:.2} KB", bytes as f64 / KB as f64)
230 } else {
231 format!("{} bytes", bytes)
232 }
233}