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