1use clap::{Args, Subcommand};
2use color_eyre::Result;
3use comfy_table::Table;
4use envx_core::{EnvVarManager, ProfileManager};
5
6#[derive(Args)]
7pub struct ProfileArgs {
8 #[command(subcommand)]
9 pub command: ProfileCommands,
10}
11
12#[derive(Subcommand)]
13pub enum ProfileCommands {
14 Create {
16 name: String,
18 #[arg(short, long)]
20 description: Option<String>,
21 },
22 List,
24 Show {
26 name: Option<String>,
28 },
29 Switch {
31 name: String,
33 #[arg(short, long)]
35 apply: bool,
36 },
37 Add {
39 profile: String,
41 name: String,
43 value: String,
45 #[arg(short, long)]
47 override_system: bool,
48 },
49 Remove {
51 profile: String,
53 name: String,
55 },
56 Delete {
58 name: String,
60 #[arg(short, long)]
62 force: bool,
63 },
64 Export {
66 name: String,
68 #[arg(short, long)]
70 output: Option<String>,
71 },
72 Import {
74 file: String,
76 #[arg(short, long)]
78 name: Option<String>,
79 #[arg(short, long)]
81 overwrite: bool,
82 },
83 Apply {
85 name: String,
87 },
88}
89
90pub fn handle_profile(args: ProfileArgs) -> Result<()> {
103 let mut profile_manager = ProfileManager::new()?;
104 let mut env_manager = EnvVarManager::new();
105 env_manager.load_all()?;
106
107 match args.command {
108 ProfileCommands::Create { name, description } => {
109 handle_profile_create(&mut profile_manager, &name, description)?;
110 }
111 ProfileCommands::List => {
112 handle_profile_list(&profile_manager);
113 }
114 ProfileCommands::Show { name } => {
115 handle_profile_show(&profile_manager, name)?;
116 }
117 ProfileCommands::Switch { name, apply } => {
118 handle_profile_switch(&mut profile_manager, &mut env_manager, &name, apply)?;
119 }
120 ProfileCommands::Add {
121 profile,
122 name,
123 value,
124 override_system,
125 } => {
126 handle_profile_add(&mut profile_manager, &profile, &name, &value, override_system)?;
127 }
128 ProfileCommands::Remove { profile, name } => {
129 handle_profile_remove(&mut profile_manager, &profile, &name)?;
130 }
131 ProfileCommands::Delete { name, force } => {
132 handle_profile_delete(&mut profile_manager, &name, force)?;
133 }
134 ProfileCommands::Export { name, output } => {
135 handle_profile_export(&profile_manager, &name, output)?;
136 }
137 ProfileCommands::Import { file, name, overwrite } => {
138 handle_profile_import(&mut profile_manager, &file, name, overwrite)?;
139 }
140 ProfileCommands::Apply { name } => {
141 handle_profile_apply(&mut profile_manager, &mut env_manager, &name)?;
142 }
143 }
144
145 Ok(())
146}
147
148fn handle_profile_create(profile_manager: &mut ProfileManager, name: &str, description: Option<String>) -> Result<()> {
149 profile_manager.create(name.to_string(), description)?;
150 println!("✅ Created profile: {name}");
151 Ok(())
152}
153
154fn handle_profile_list(profile_manager: &ProfileManager) {
155 let profiles = profile_manager.list();
156 if profiles.is_empty() {
157 println!("No profiles found.");
158 }
159
160 let active = profile_manager.active().map(|p| &p.name);
161 let mut table = Table::new();
162 table.set_header(vec!["Name", "Variables", "Created", "Description", "Status"]);
163
164 for profile in profiles {
165 let status = if active == Some(&profile.name) {
166 "● Active"
167 } else {
168 ""
169 };
170
171 table.add_row(vec![
172 profile.name.clone(),
173 profile.variables.len().to_string(),
174 profile.created_at.format("%Y-%m-%d").to_string(),
175 profile.description.clone().unwrap_or_default(),
176 status.to_string(),
177 ]);
178 }
179
180 println!("{table}");
181}
182
183fn handle_profile_show(profile_manager: &ProfileManager, name: Option<String>) -> Result<()> {
184 let profile = if let Some(name) = name {
185 profile_manager
186 .get(&name)
187 .ok_or_else(|| color_eyre::eyre::eyre!("Profile '{}' not found", name))?
188 } else {
189 profile_manager
190 .active()
191 .ok_or_else(|| color_eyre::eyre::eyre!("No active profile"))?
192 };
193
194 println!("Profile: {}", profile.name);
195 println!("Description: {}", profile.description.as_deref().unwrap_or(""));
196 println!("Created: {}", profile.created_at.format("%Y-%m-%d %H:%M:%S"));
197 println!("Updated: {}", profile.updated_at.format("%Y-%m-%d %H:%M:%S"));
198 if let Some(parent) = &profile.parent {
199 println!("Inherits from: {parent}");
200 }
201 println!("\nVariables:");
202
203 for (name, var) in &profile.variables {
204 let status = if var.enabled { "✓" } else { "✗" };
205 let override_flag = if var.override_system { " [override]" } else { "" };
206 println!(" {} {} = {}{}", status, name, var.value, override_flag);
207 }
208 Ok(())
209}
210
211fn handle_profile_switch(
212 profile_manager: &mut ProfileManager,
213 env_manager: &mut EnvVarManager,
214 name: &str,
215 apply: bool,
216) -> Result<()> {
217 profile_manager.switch(name)?;
218 println!("✅ Switched to profile: {name}");
219
220 if apply {
221 profile_manager.apply(name, env_manager)?;
222 println!("✅ Applied profile variables");
223 }
224 Ok(())
225}
226
227fn handle_profile_add(
228 profile_manager: &mut ProfileManager,
229 profile: &str,
230 name: &str,
231 value: &str,
232 override_system: bool,
233) -> Result<()> {
234 let prof = profile_manager
235 .get_mut(profile)
236 .ok_or_else(|| color_eyre::eyre::eyre!("Profile '{}' not found", profile))?;
237
238 prof.add_var(name.to_string(), value.to_string(), override_system);
239 profile_manager.save()?;
240
241 println!("✅ Added {name} to profile {profile}");
242 Ok(())
243}
244
245fn handle_profile_remove(profile_manager: &mut ProfileManager, profile: &str, name: &str) -> Result<()> {
246 let prof = profile_manager
247 .get_mut(profile)
248 .ok_or_else(|| color_eyre::eyre::eyre!("Profile '{}' not found", profile))?;
249
250 prof.remove_var(name)
251 .ok_or_else(|| color_eyre::eyre::eyre!("Variable '{}' not found in profile", name))?;
252
253 profile_manager.save()?;
254 println!("✅ Removed {name} from profile {profile}");
255 Ok(())
256}
257
258fn handle_profile_delete(profile_manager: &mut ProfileManager, name: &str, force: bool) -> Result<()> {
259 if !force {
260 print!("⚠️ Delete profile '{name}'? [y/N] ");
261 std::io::Write::flush(&mut std::io::stdout())?;
262
263 let mut input = String::new();
264 std::io::stdin().read_line(&mut input)?;
265 if !input.trim().eq_ignore_ascii_case("y") {
266 println!("Cancelled.");
267 return Ok(());
268 }
269 }
270
271 profile_manager.delete(name)?;
272 println!("✅ Deleted profile: {name}");
273 Ok(())
274}
275
276fn handle_profile_export(profile_manager: &ProfileManager, name: &str, output: Option<String>) -> Result<()> {
277 let json = profile_manager.export(name)?;
278
279 if let Some(path) = output {
280 std::fs::write(path, json)?;
281 println!("✅ Exported profile to file");
282 } else {
283 println!("{json}");
284 }
285 Ok(())
286}
287
288fn handle_profile_import(
289 profile_manager: &mut ProfileManager,
290 file: &str,
291 name: Option<String>,
292 overwrite: bool,
293) -> Result<()> {
294 let content = std::fs::read_to_string(file)?;
295 let import_name = name.unwrap_or_else(|| "imported".to_string());
296
297 profile_manager.import(import_name.clone(), &content, overwrite)?;
298 println!("✅ Imported profile: {import_name}");
299 Ok(())
300}
301
302fn handle_profile_apply(
303 profile_manager: &mut ProfileManager,
304 env_manager: &mut EnvVarManager,
305 name: &str,
306) -> Result<()> {
307 profile_manager.apply(name, env_manager)?;
308 println!("✅ Applied profile: {name}");
309 Ok(())
310}