vx_cli/commands/
venv_cmd.rs1use crate::ui::UI;
4use clap::{Args, Subcommand};
5use vx_core::{Result, VenvManager};
6
7#[derive(Args)]
8pub struct VenvArgs {
9 #[command(subcommand)]
10 pub command: VenvCommand,
11}
12
13#[derive(Subcommand, Clone)]
14pub enum VenvCommand {
15 Create {
17 name: String,
19 #[arg(short, long, value_delimiter = ',')]
21 tools: Vec<String>,
22 },
23 List,
25 Activate {
27 name: String,
29 },
30 Deactivate,
32 Remove {
34 name: String,
36 #[arg(short, long)]
38 force: bool,
39 },
40 Current,
42}
43
44pub async fn handle(command: VenvCommand) -> Result<()> {
45 let manager = VenvManager::new()?;
46
47 match command {
48 VenvCommand::Create { name, tools } => create_venv(&manager, &name, &tools).await,
49 VenvCommand::List => list_venvs(&manager).await,
50 VenvCommand::Activate { name } => activate_venv(&manager, &name).await,
51 VenvCommand::Deactivate => deactivate_venv().await,
52 VenvCommand::Remove { name, force } => remove_venv(&manager, &name, force).await,
53 VenvCommand::Current => show_current_venv().await,
54 }
55}
56
57async fn create_venv(manager: &VenvManager, name: &str, tools: &[String]) -> Result<()> {
58 UI::info(&format!("Creating virtual environment '{name}'"));
59
60 let mut tool_specs = Vec::new();
62 for tool_spec in tools {
63 if let Some((tool, version)) = tool_spec.split_once('@') {
64 tool_specs.push((tool.to_string(), version.to_string()));
65 } else {
66 tool_specs.push((tool_spec.clone(), "latest".to_string()));
67 }
68 }
69
70 if tool_specs.is_empty() {
71 UI::warning("No tools specified. Creating empty virtual environment.");
72 } else {
73 UI::info("Tools to install:");
74 for (tool, version) in &tool_specs {
75 UI::detail(&format!(" {} @ {}", tool, version));
76 }
77 }
78
79 match manager.create(name, &tool_specs) {
81 Ok(()) => {
82 UI::success(&format!(
83 "Virtual environment '{}' created successfully!",
84 name
85 ));
86 UI::hint(&format!("Activate with: vx venv activate {}", name));
87 }
88 Err(e) => {
89 UI::error(&format!(
90 "Failed to create virtual environment '{}': {}",
91 name, e
92 ));
93 return Err(e);
94 }
95 }
96
97 Ok(())
98}
99
100async fn list_venvs(manager: &VenvManager) -> Result<()> {
101 UI::header("Virtual Environments");
102
103 let venvs = match manager.list() {
104 Ok(venvs) => venvs,
105 Err(e) => {
106 UI::error(&format!("Failed to list virtual environments: {}", e));
107 return Err(e);
108 }
109 };
110
111 if venvs.is_empty() {
112 UI::info("No virtual environments found.");
113 UI::hint("Create one with: vx venv create <name>");
114 return Ok(());
115 }
116
117 let current = VenvManager::current();
118
119 for venv in venvs {
120 if Some(&venv) == current.as_ref() {
121 UI::success(&format!("* {} (active)", venv));
122 } else {
123 UI::info(&format!(" {}", venv));
124 }
125 }
126
127 if let Some(current) = current {
128 UI::detail(&format!("Currently active: {}", current));
129 } else {
130 UI::hint("Activate an environment with: vx venv activate <name>");
131 }
132
133 Ok(())
134}
135
136async fn activate_venv(manager: &VenvManager, name: &str) -> Result<()> {
137 UI::info(&format!("Activating virtual environment '{}'", name));
138
139 if VenvManager::is_active() {
141 if let Some(current) = VenvManager::current() {
142 if current == name {
143 UI::warning(&format!("Virtual environment '{}' is already active", name));
144 return Ok(());
145 } else {
146 UI::warning(&format!("Deactivating current environment '{}'", current));
147 }
148 }
149 }
150
151 let activation_script = match manager.activate(name) {
153 Ok(script) => script,
154 Err(e) => {
155 UI::error(&format!(
156 "Failed to activate virtual environment '{}': {}",
157 name, e
158 ));
159 return Err(e);
160 }
161 };
162
163 UI::success(&format!("Activating virtual environment '{}'", name));
164 UI::info("Run the following commands in your shell:");
165 println!();
166 println!("{}", activation_script);
167 println!();
168 UI::hint(&format!(
169 "Copy and paste the above commands, or use: eval \"$(vx venv activate {})\"",
170 name
171 ));
172
173 Ok(())
174}
175
176async fn deactivate_venv() -> Result<()> {
177 UI::info("Deactivating virtual environment");
178
179 if !VenvManager::is_active() {
180 UI::warning("No virtual environment is currently active");
181 return Ok(());
182 }
183
184 let current = VenvManager::current().unwrap();
185 let deactivation_script = VenvManager::deactivate();
186
187 UI::success(&format!("Deactivating virtual environment '{}'", current));
188 UI::info("Run the following commands in your shell:");
189 println!();
190 println!("{}", deactivation_script);
191 println!();
192 UI::hint("Copy and paste the above commands, or use: eval \"$(vx venv deactivate)\"");
193
194 Ok(())
195}
196
197async fn remove_venv(manager: &VenvManager, name: &str, force: bool) -> Result<()> {
198 UI::info(&format!("Removing virtual environment '{}'", name));
199
200 if let Some(current) = VenvManager::current() {
202 if current == name {
203 UI::error("Cannot remove active virtual environment. Deactivate first.");
204 UI::hint("Run: vx venv deactivate");
205 return Ok(());
206 }
207 }
208
209 if !force {
210 UI::warning(&format!(
211 "This will permanently delete virtual environment '{}'",
212 name
213 ));
214 UI::info("Use --force to confirm removal");
215 return Ok(());
216 }
217
218 match manager.remove(name) {
220 Ok(()) => {
221 UI::success(&format!(
222 "Virtual environment '{}' removed successfully",
223 name
224 ));
225 }
226 Err(e) => {
227 UI::error(&format!(
228 "Failed to remove virtual environment '{}': {}",
229 name, e
230 ));
231 return Err(e);
232 }
233 }
234
235 Ok(())
236}
237
238async fn show_current_venv() -> Result<()> {
239 UI::header("Current Virtual Environment");
240
241 if let Some(current) = VenvManager::current() {
242 UI::success(&format!("Current virtual environment: {}", current));
243 UI::detail("Environment is active");
244 } else {
245 UI::info("No virtual environment is currently active");
246 UI::hint("Activate one with: vx venv activate <name>");
247 }
248
249 Ok(())
250}