proc_cli/commands/
stop.rs1use crate::core::{resolve_target, Process};
9use crate::error::{ProcError, Result};
10use crate::ui::{OutputFormat, Printer};
11use clap::Args;
12use dialoguer::Confirm;
13use serde::Serialize;
14
15#[derive(Args, Debug)]
17pub struct StopCommand {
18 #[arg(required = true)]
20 target: String,
21
22 #[arg(long, short = 'y')]
24 yes: bool,
25
26 #[arg(long, short)]
28 json: bool,
29
30 #[arg(long, short, default_value = "10")]
32 timeout: u64,
33}
34
35impl StopCommand {
36 pub fn execute(&self) -> Result<()> {
37 let format = if self.json {
38 OutputFormat::Json
39 } else {
40 OutputFormat::Human
41 };
42 let printer = Printer::new(format, false);
43
44 let processes = self.find_target_processes()?;
46
47 if processes.is_empty() {
48 return Err(ProcError::ProcessNotFound(self.target.clone()));
49 }
50
51 if !self.yes && !self.json {
53 self.show_processes(&processes);
54
55 let prompt = format!(
56 "Stop {} process{}?",
57 processes.len(),
58 if processes.len() == 1 { "" } else { "es" }
59 );
60
61 if !Confirm::new()
62 .with_prompt(prompt)
63 .default(false)
64 .interact()?
65 {
66 printer.warning("Aborted");
67 return Ok(());
68 }
69 }
70
71 let mut stopped = Vec::new();
73 let mut failed = Vec::new();
74
75 for proc in &processes {
76 match proc.terminate() {
77 Ok(()) => {
78 let stopped_gracefully = self.wait_for_exit(proc);
80 if stopped_gracefully {
81 stopped.push(proc.clone());
82 } else {
83 match proc.kill_and_wait() {
85 Ok(_) => stopped.push(proc.clone()),
86 Err(e) => failed.push((proc.clone(), e.to_string())),
87 }
88 }
89 }
90 Err(e) => failed.push((proc.clone(), e.to_string())),
91 }
92 }
93
94 if self.json {
96 printer.print_json(&StopOutput {
97 action: "stop",
98 success: failed.is_empty(),
99 stopped_count: stopped.len(),
100 failed_count: failed.len(),
101 stopped: &stopped,
102 failed: &failed
103 .iter()
104 .map(|(p, e)| FailedStop {
105 process: p,
106 error: e,
107 })
108 .collect::<Vec<_>>(),
109 });
110 } else {
111 self.print_results(&printer, &stopped, &failed);
112 }
113
114 Ok(())
115 }
116
117 fn find_target_processes(&self) -> Result<Vec<Process>> {
118 resolve_target(&self.target)
119 }
120
121 fn wait_for_exit(&self, proc: &Process) -> bool {
122 let start = std::time::Instant::now();
123 let timeout = std::time::Duration::from_secs(self.timeout);
124
125 while start.elapsed() < timeout {
126 if !proc.is_running() {
127 return true;
128 }
129 std::thread::sleep(std::time::Duration::from_millis(100));
130 }
131
132 false
133 }
134
135 fn show_processes(&self, processes: &[Process]) {
136 use colored::*;
137
138 println!(
139 "\n{} Found {} process{}:\n",
140 "!".yellow().bold(),
141 processes.len().to_string().cyan().bold(),
142 if processes.len() == 1 { "" } else { "es" }
143 );
144
145 for proc in processes {
146 println!(
147 " {} {} [PID {}] - {:.1}% CPU, {:.1} MB",
148 "→".bright_black(),
149 proc.name.white().bold(),
150 proc.pid.to_string().cyan(),
151 proc.cpu_percent,
152 proc.memory_mb
153 );
154 }
155 println!();
156 }
157
158 fn print_results(&self, printer: &Printer, stopped: &[Process], failed: &[(Process, String)]) {
159 use colored::*;
160
161 if !stopped.is_empty() {
162 println!(
163 "{} Stopped {} process{}",
164 "✓".green().bold(),
165 stopped.len().to_string().cyan().bold(),
166 if stopped.len() == 1 { "" } else { "es" }
167 );
168 for proc in stopped {
169 println!(
170 " {} {} [PID {}]",
171 "→".bright_black(),
172 proc.name.white(),
173 proc.pid.to_string().cyan()
174 );
175 }
176 }
177
178 if !failed.is_empty() {
179 printer.error(&format!(
180 "Failed to stop {} process{}",
181 failed.len(),
182 if failed.len() == 1 { "" } else { "es" }
183 ));
184 for (proc, err) in failed {
185 println!(
186 " {} {} [PID {}]: {}",
187 "→".bright_black(),
188 proc.name.white(),
189 proc.pid.to_string().cyan(),
190 err.red()
191 );
192 }
193 }
194 }
195}
196
197#[derive(Serialize)]
198struct StopOutput<'a> {
199 action: &'static str,
200 success: bool,
201 stopped_count: usize,
202 failed_count: usize,
203 stopped: &'a [Process],
204 failed: &'a [FailedStop<'a>],
205}
206
207#[derive(Serialize)]
208struct FailedStop<'a> {
209 process: &'a Process,
210 error: &'a str,
211}