claude_code_switcher/
utils.rs

1use anyhow::{Result, anyhow};
2use console::style;
3use inquire::Confirm;
4use std::path::{Path, PathBuf};
5
6use crate::settings::ClaudeSettings;
7
8/// Get the path to the settings file
9pub 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
16/// Get the path to the environment-specific settings file
17pub fn get_env_var_path() -> PathBuf {
18    PathBuf::from(".claude").join("settings.json")
19}
20
21/// Get the snapshots directory
22pub 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
27/// Get the credentials directory
28pub 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
33/// Confirm an action with the user
34pub fn confirm_action(message: &str, default: bool) -> Result<bool> {
35    Ok(Confirm::new(message).with_default(default).prompt()?)
36}
37
38/// Create a backup of current settings
39pub 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
50/// Restore settings from backup
51pub 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
67/// Get the current working directory's claude settings path
68pub fn get_local_settings_path() -> PathBuf {
69    PathBuf::from(".claude").join("settings.json")
70}
71
72/// Check if we should use local or global settings
73pub 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    // Check for .claude directory in current working directory
80    let local_claude_dir = PathBuf::from(".claude");
81    local_claude_dir.exists()
82}
83
84/// Format bytes to human readable format
85pub 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
106/// Format duration to human readable format
107pub 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
122/// Truncate text to a maximum length
123pub 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
131/// Get a colored status indicator
132pub 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
140/// Format a list of items for display
141pub fn format_list(items: &[&str], separator: &str) -> String {
142    items.join(separator)
143}
144
145/// Get the file size of a path
146pub 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
156/// Ensure a directory exists
157pub 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
165/// Check if a string is a valid UUID
166pub fn is_valid_uuid(uuid_str: &str) -> bool {
167    uuid::Uuid::parse_str(uuid_str).is_ok()
168}
169
170/// Get timestamp for display
171pub fn get_timestamp() -> String {
172    let now = chrono::Utc::now();
173    now.format("%Y-%m-%d %H:%M:%S UTC").to_string()
174}
175
176/// Format settings summary for display
177pub 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}