claude_code_switcher/
utils.rs1use anyhow::{Result, anyhow};
2use console::style;
3use inquire::Confirm;
4use std::path::{Path, PathBuf};
5
6use crate::settings::ClaudeSettings;
7
8pub fn get_settings_path(settings_path: Option<PathBuf>) -> PathBuf {
10 settings_path.unwrap_or_else(|| {
11 let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
12 home_dir.join(".claude").join("settings.json")
13 })
14}
15
16pub fn get_env_var_path() -> PathBuf {
18 PathBuf::from(".claude").join("settings.json")
19}
20
21pub fn get_snapshots_dir() -> PathBuf {
23 let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
24 home_dir.join(".claude").join("snapshots")
25}
26
27pub fn get_credentials_dir() -> PathBuf {
29 let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
30 home_dir.join(".claude").join("credentials")
31}
32
33pub fn confirm_action(message: &str, default: bool) -> Result<bool> {
35 Ok(Confirm::new(message).with_default(default).prompt()?)
36}
37
38pub fn backup_settings(settings_path: &Path) -> Result<Option<PathBuf>> {
40 if !settings_path.exists() {
41 return Ok(None);
42 }
43
44 let backup_path = settings_path.with_extension("json.backup");
45 std::fs::copy(settings_path, &backup_path)
46 .map_err(|e| anyhow!("Failed to create backup: {}", e))?;
47 Ok(Some(backup_path))
48}
49
50pub fn restore_from_backup(settings_path: &Path) -> Result<()> {
52 let backup_path = settings_path.with_extension("json.backup");
53
54 if !backup_path.exists() {
55 return Err(anyhow!("Backup file not found: {}", backup_path.display()));
56 }
57
58 std::fs::copy(&backup_path, settings_path)
59 .map_err(|e| anyhow!("Failed to restore from backup: {}", e))?;
60
61 std::fs::remove_file(&backup_path)
62 .map_err(|e| anyhow!("Failed to remove backup file: {}", e))?;
63
64 Ok(())
65}
66
67pub fn get_local_settings_path() -> PathBuf {
69 PathBuf::from(".claude").join("settings.json")
70}
71
72pub fn should_use_local_settings() -> bool {
74 let local_path = get_local_settings_path();
75 if local_path.exists() {
76 return true;
77 }
78
79 let local_claude_dir = PathBuf::from(".claude");
81 local_claude_dir.exists()
82}
83
84pub fn format_bytes(bytes: u64) -> String {
86 const UNITS: &[&str] = &["B", "KB", "MB", "GB", "TB"];
87 if bytes == 0 {
88 return "0 B".to_string();
89 }
90
91 let mut size = bytes as f64;
92 let mut unit_index = 0;
93
94 while size >= 1024.0 && unit_index < UNITS.len() - 1 {
95 size /= 1024.0;
96 unit_index += 1;
97 }
98
99 if unit_index == 0 {
100 format!("{} {}", bytes, UNITS[unit_index])
101 } else {
102 format!("{:.1} {}", size, UNITS[unit_index])
103 }
104}
105
106pub fn format_duration(seconds: i64) -> String {
108 if seconds < 60 {
109 format!("{}s", seconds)
110 } else if seconds < 3600 {
111 format!("{}m {}s", seconds / 60, seconds % 60)
112 } else {
113 format!(
114 "{}h {}m {}s",
115 seconds / 3600,
116 (seconds % 3600) / 60,
117 seconds % 60
118 )
119 }
120}
121
122pub fn truncate_text(text: &str, max_length: usize) -> String {
124 if text.len() <= max_length {
125 text.to_string()
126 } else {
127 format!("{}...", &text[..max_length.saturating_sub(3)])
128 }
129}
130
131pub fn status_indicator(success: bool, message: &str) -> String {
133 if success {
134 format!("{} {}", style("✓").green().bold(), message)
135 } else {
136 format!("{} {}", style("✗").red().bold(), message)
137 }
138}
139
140pub fn format_list(items: &[&str], separator: &str) -> String {
142 items.join(separator)
143}
144
145pub fn get_file_size(path: &Path) -> Result<u64> {
147 if path.exists() {
148 let metadata =
149 std::fs::metadata(path).map_err(|e| anyhow!("Failed to get file metadata: {}", e))?;
150 Ok(metadata.len())
151 } else {
152 Ok(0)
153 }
154}
155
156pub fn ensure_dir_exists(dir: &Path) -> Result<()> {
158 if !dir.exists() {
159 std::fs::create_dir_all(dir)
160 .map_err(|e| anyhow!("Failed to create directory {}: {}", dir.display(), e))?;
161 }
162 Ok(())
163}
164
165pub fn is_valid_uuid(uuid_str: &str) -> bool {
167 uuid::Uuid::parse_str(uuid_str).is_ok()
168}
169
170pub fn get_timestamp() -> String {
172 let now = chrono::Utc::now();
173 now.format("%Y-%m-%d %H:%M:%S UTC").to_string()
174}
175
176pub fn format_settings_summary(settings: &ClaudeSettings) -> String {
178 let mut summary = String::new();
179
180 if let Some(ref provider) = settings.provider {
181 summary.push_str(&format!("Provider: {}\n", provider.id));
182 }
183
184 if let Some(ref model) = settings.model {
185 summary.push_str(&format!("Model: {}\n", model.name));
186 }
187
188 if let Some(ref endpoint) = settings.endpoint {
189 summary.push_str(&format!(
190 "Endpoint: {} ({})\n",
191 endpoint.id, endpoint.api_base
192 ));
193 }
194
195 if let Some(ref http) = settings.http {
196 if let Some(timeout) = http.timeout_ms {
197 summary.push_str(&format!("Timeout: {}ms\n", timeout));
198 }
199 }
200
201 if let Some(ref permissions) = settings.permissions {
202 if let Some(network_access) = permissions.allow_network_access {
203 summary.push_str(&format!(
204 "Network Access: {}\n",
205 if network_access { "Allowed" } else { "Denied" }
206 ));
207 }
208 }
209
210 summary.trim_end().to_string()
211}