cc_switch/config/config_storage.rs
1use anyhow::{Context, Result};
2use std::fs;
3
4use crate::config::config::get_config_storage_path;
5use crate::config::types::{ConfigStorage, Configuration};
6
7impl ConfigStorage {
8 /// Load configurations from disk
9 ///
10 /// Reads the JSON file from `~/.claude/cc_auto_switch_setting.json`
11 /// Auto-migrates from old location `~/.cc-switch/configurations.json` if it exists
12 /// Returns default empty storage if file doesn't exist
13 ///
14 /// # Errors
15 /// Returns error if file exists but cannot be read or parsed
16 pub fn load() -> Result<Self> {
17 let new_path = get_config_storage_path()?;
18
19 // Check if the new file already exists
20 if new_path.exists() {
21 let content = fs::read_to_string(&new_path).with_context(|| {
22 format!(
23 "Failed to read configuration storage from {}",
24 new_path.display()
25 )
26 })?;
27
28 let storage: ConfigStorage = serde_json::from_str(&content)
29 .with_context(|| "Failed to parse configuration storage JSON")?;
30
31 return Ok(storage);
32 }
33
34 // No configuration file exists at new path, return default empty storage
35 Ok(ConfigStorage::default())
36 }
37
38 /// Save configurations to disk
39 ///
40 /// Writes the current state to `~/.claude/cc_auto_switch_setting.json`
41 /// Creates the directory structure if it doesn't exist
42 ///
43 /// # Errors
44 /// Returns error if directory cannot be created or file cannot be written
45 pub fn save(&self) -> Result<()> {
46 let path = get_config_storage_path()?;
47
48 // Create directory if it doesn't exist
49 if let Some(parent) = path.parent() {
50 fs::create_dir_all(parent)
51 .with_context(|| format!("Failed to create directory {}", parent.display()))?;
52 }
53
54 let json = serde_json::to_string_pretty(self)
55 .with_context(|| "Failed to serialize configuration storage")?;
56
57 fs::write(&path, json).with_context(|| format!("Failed to write to {}", path.display()))?;
58
59 Ok(())
60 }
61
62 /// Migrate configurations from old path to new path
63 ///
64 /// Old path: `~/.cc_auto_switch/configurations.json`
65 /// New path: `~/.claude/cc_auto_switch_setting.json`
66 ///
67 /// Safe to run multiple times. If old path does not exist, returns Ok(()) and prints a note.
68 pub fn migrate_from_old_path() -> Result<()> {
69 let new_path = get_config_storage_path()?;
70 let old_path = dirs::home_dir()
71 .map(|home| home.join(".cc_auto_switch").join("configurations.json"))
72 .ok_or_else(|| anyhow::anyhow!("Could not find home directory"))?;
73
74 if !old_path.exists() {
75 println!("âšī¸ No old configuration found at {}", old_path.display());
76 return Ok(());
77 }
78
79 println!("đ Migrating configuration from old location...");
80
81 let content = fs::read_to_string(&old_path).with_context(|| {
82 format!(
83 "Failed to read old configuration from {}",
84 old_path.display()
85 )
86 })?;
87
88 let storage: ConfigStorage = serde_json::from_str(&content)
89 .with_context(|| "Failed to parse old configuration storage JSON")?;
90
91 // Save to new location
92 storage
93 .save()
94 .with_context(|| "Failed to save migrated configuration to new location")?;
95
96 // Remove old directory
97 if let Some(parent) = old_path.parent() {
98 fs::remove_dir_all(parent).with_context(|| {
99 format!(
100 "Failed to remove old configuration directory {}",
101 parent.display()
102 )
103 })?;
104 }
105
106 println!(
107 "â
Configuration migrated successfully to {}",
108 new_path.display()
109 );
110
111 Ok(())
112 }
113
114 /// Add a new configuration to storage
115 ///
116 /// # Arguments
117 /// * `config` - Configuration object to add
118 ///
119 /// Overwrites existing configuration with same alias
120 pub fn add_configuration(&mut self, config: Configuration) {
121 self.configurations
122 .insert(config.alias_name.clone(), config);
123 }
124
125 /// Remove a configuration by alias name
126 ///
127 /// # Arguments
128 /// * `alias_name` - Name of configuration to remove
129 ///
130 /// # Returns
131 /// `true` if configuration was found and removed, `false` if not found
132 pub fn remove_configuration(&mut self, alias_name: &str) -> bool {
133 self.configurations.remove(alias_name).is_some()
134 }
135
136 /// Get a configuration by alias name
137 ///
138 /// # Arguments
139 /// * `alias_name` - Name of configuration to retrieve
140 ///
141 /// # Returns
142 /// `Some(&Configuration)` if found, `None` if not found
143 pub fn get_configuration(&self, alias_name: &str) -> Option<&Configuration> {
144 self.configurations.get(alias_name)
145 }
146
147 /// Set the default directory for Claude settings
148 ///
149 /// # Arguments
150 /// * `directory` - Directory path for Claude settings
151 #[allow(dead_code)]
152 pub fn set_claude_settings_dir(&mut self, directory: String) {
153 self.claude_settings_dir = Some(directory);
154 }
155
156 /// Get the current Claude settings directory
157 ///
158 /// # Returns
159 /// `Some(&String)` if custom directory is set, `None` if using default
160 #[allow(dead_code)]
161 pub fn get_claude_settings_dir(&self) -> Option<&String> {
162 self.claude_settings_dir.as_ref()
163 }
164
165 /// Update an existing configuration
166 ///
167 /// This method handles updating a configuration, including potential alias renaming.
168 /// If the new configuration has a different alias name than the old one, it removes
169 /// the old entry and creates a new one.
170 ///
171 /// # Arguments
172 /// * `old_alias` - Current alias name of the configuration to update
173 /// * `new_config` - Updated configuration object
174 ///
175 /// # Returns
176 /// `Ok(())` if update succeeds, `Err` if the old configuration doesn't exist
177 ///
178 /// # Errors
179 /// Returns error if the configuration with `old_alias` doesn't exist
180 pub fn update_configuration(
181 &mut self,
182 old_alias: &str,
183 new_config: Configuration,
184 ) -> Result<()> {
185 // Check if the old configuration exists
186 if !self.configurations.contains_key(old_alias) {
187 return Err(anyhow::anyhow!("Configuration '{}' not found", old_alias));
188 }
189
190 // If alias changed, remove the old entry
191 if old_alias != new_config.alias_name {
192 self.configurations.remove(old_alias);
193 }
194
195 // Insert the updated configuration (this will overwrite if alias hasn't changed)
196 self.configurations
197 .insert(new_config.alias_name.clone(), new_config);
198
199 Ok(())
200 }
201}